Symbolic Constants

Constants are values that do not change during the life of the program. Constants are like variables in that the name of the constant refers to the defined value, but unlike a variable, these values cannot be changed in the program. There are two important reasons why constants should be used in your program.

The first reason is that they help to document the program. Suppose in your role-playing game you have a weapon such as a broadsword. If you define the broadsword as a constant, which you can do with #Define broadsword 12, you can then refer to the weapon id as broadsword, rather than 12. The number 12 imparts no real information when you see it in the code; broadsword on the the other hand is quite clear and understandable.

The second reason to use a constant is code maintenance. There may come a time when working on your role-playing game that you need to change the value of the broadsword id. If you have defined the id as a constant, you only need to change it in a single location, the place where you defined the constant. If you had just used the number 12, you would have to search through the code and change each instance where 12 referred to the broadsword. If the program is of any length at all, you will probably miss a reference or two, introducing bugs into your program. Bugs that may be difficult to locate and fix.

One of the things you will discover as you progress in your programming adventure is that programs are dynamic, not static. There is always a new technique being developed that you can use, a new compiler function that will improve your program, and bugs that need to be fixed. The only thing that stays the same in a program is the fact that programs continually evolve over time. You should always keep in mind that when you write a program, you will probably end up making changes to the program, and you should code accordingly. It may be easier to write 12 than it is to write broadsword, but a few extra seconds of typing will save you hours when you need to change 12 to 120.

#Define as a Constant

#Define is a preprocessor command, where the defined symbol is physically replaced in the code by the associated value. #Define of course is used in a number of situations, from creating macros to conditional compilation, but it is also useful for creating constants within your program.

If you look through the declaration files in the include folder of your FreeBasic installation, you will see that #Define is used extensively for constant values. Since the compiler replaces the symbol definition with the defined value, it is a very efficient coding method. It is also quite easy to use, as the following code snippet illustrates.

'Define directions
#Define north 1
#Define neast 2
#Define east 3
#Define seast 4
#Define south 5
#Define west 6
#Define swest 7
#Define nwest 8

Once you define the constants, you can use the symbols in your programs just as you would the values.

If pdir = north Then
    'do something
End If

The Const Keyword

The Const keyword is another method to define constants in your program. The format is similar to the #define, as the following code snippet illustrates.

Const xk = Chr(255)
Const key_up = xk & Chr(72)
Const key_dn = xk & Chr(80)
Const key_rt = xk & Chr(77)
Const key_lt = xk & Chr(75)

These constants are the character codes returned by Inkey for the arrow keys. Inkey returns a two byte string for the extended keys, Chr(255) + the extended key character code. To use these constaints in your program you would just use key_lt for example to check to see if the left arrow key had been pressed.

Const Versus #Define

As you can see Const and #Define are similar constructs. The question then becomes, which one should you use? Remember that #Define replaces the symbol name with the text following the symbol. If you wrote #Define key_up xk & Chr(72), then the code xk & Chr(72) would be replace the symbol key_up. If you had several places where you used key_up, then there would be several instances of xk & Chr(72) in your program. You can see that by using #Define in this case your program would be performing the same calculation over and over. It is much more efficient to use Const in this case, since the calculation is only done once, when the Const is defined.

On the other hand, if you are defining single constant values, such as compass directions, then using a #Define is preferable to using Const. For a Const value, the compiler must do a lookup in the symbol table and it is much more efficient to simple replace the symbol with the value using #Define.

Enumerations

Enumerations are sequential values that the compiler can calculate for you. To create an enumeration, you use enclose the values within an Enum-End Enum block. The compass direction defined above could also be defined as an enumeration.

Enum compass
    north = 1
    neast
    east
    seast
    south
    west
    swest
    nwest
End Enum

In this example, neast will be defined as 2, with east defined as 3 and so on. If no starting value is set, enumerations start at 0. You can also change the sequence within an enumeration by setting a symbol to a value using =, and any following symbols will be incremented from this starting point.

Enum compass
    north = 1
    east
    south
    west
    neast = 10
    seast
    swest
    nwest
End Enum

In this example, the value of seast will be 11, swest will be 12 and so on. Once you define an enumeration, you can create variables of the Enum and use that variable within your program.

Dim aCompass as compass

You can then use the enumeration values to initialize the variable. The following code snippet set aCompass to the north-defined value.

aCompass = north

The compiler does not check to see if the value being passed to the Enum variable is within the defined range of the enumeration. It is the responsibility of the programmer to ensure that the Enum variable contains the correct values.

The Preprocessor

FreeBasic has a preprocessor much like the C programming language. The preprocessor directives allow you to perform conditional compilations and to create macros within your program. The following table lists the preprocessor directives.

Directive Syntax Comments
#define #define symbol
#define text
#define macro()
Defines a symbol, text or macro.
#else #else Used within an #if, #ifdef or #ifndef to provide alternate results.
#elseif #elseif (expression) Provides an additonal #if qualifier.
#endif #endif Closes an #if, #ifdef or #ifndef block.
#endmacro #endmacro Closes a macro block.
#error #error error_text Stops compilation and displays error text.
#if #if (expression) Conditionally includes statements within #if and #endif if expression is True.
#ifdef #ifdef symbol Conditionally includes statements within #ifdef and #endif if symbol exists.
#ifndef #ifndef symbol Conditionally includes statements within #ifdef and #endif if symbol does not exist.
#inclib #inclib "filename" Includes library file in the linking process. Library files are pre-compiled code modules ending with the .a extension.
#include #include [once] "filename" Includes inserts a source code module into the current source. The once identifier will prevent multiple files from being loaded.
#libpath #libpath path Adds a library search path to the linker search path.
#line #line number ["name"] Changes the line number and file name in the __FILE__ and __LINE__ macros.
#macro #macro name[(parm1 [, parm2…])] Starts a macro block which is ended with #endmacro.
#print #print text Prints text during compilation.
#undef #undef symbol Erases a symbol definiton previously created using #define.

Conditional Compilation

Using the processor directives, you can create different versions of your program. You may want to have a debugging version and a release version and by using the listed directives, you can create code blocks that will only get compiled under certain situations. The following example demonstrates this concept.

#define debug

#ifdef debug
    print "Variable value is ";myVar
#endif

Another example is in creating constants within your program.

#ifndef False
    #define False 0
    #define True (Not False)
#endif

Creating and Using Macros

A macro is a small piece of code that is defined in your program using either the #Define directive or by using the #Macro and #EndMacro directives. Macros are used to create inline code that can be executed without the overhead of a function call.

Like a function, a macro can take parameters and operate on the parameters returning a value. The parameters types are defined by the compiler based on the data type that is used when the macro is called. Often times you will need to be sure the passed parameters are of a certain type, so you should use the built-in conversion functions to cast the parameters to the needed type.

The following code snippet shows the definition of the RGB macro.

#define RGB(R,G,B) (((R)Shl 16)  Or ((G)Shl 8) Or (B))

Once defined, you would call the macro just like a function.

myColor = RGB(255, 234, 200)

To create a multi-line macro you would use the #Macro and #EndMacro directives. The following macro definition swaps two values using the Xor operator.

#macro XSwap(a, b)
a = a Xor b
b = b Xor a
a = a Xor b
#endmacro

Once defined you would call this macro as:

XSwap(a, b)

Macros can be viewed as small programs, and as such you can Dim variables within a macro and use them in the code as the following example illustrates.

#macro TSwap(a, b)
Scope
Dim tmp As Integer
tmp = b
b = a
a = tmp
End Scope
#endmacro

Here the Scope and End Scope keywords are used to create a temporary scope block for the variable tmp. Using Scope-End Scope will prevent name clashes within your program and is recommended whenever you need to create variables within your macros.

Built-In Macros

FreeBasic has a number of built-in macros that you can use in your programs. These are help in a number of areas from version checking to debugging. The following lists all the macros currently available. All the macros have a double underscore before and after the macro name.

Macro Comment
__DATE__ Returns the compiler data.
__FB_ARGC__ Reurns the number of arguments passed on the command line.
__FB_ARGV__ Returns a pointer to a list of pointers of Null terminated command line arguments.
__FB_BIGENDIAN__ Used to set the compiler to big endian.
__FB_BUILD_DATE__ Returns the the date of the compiler build.
__FB_DEBUG__ Indicates whether the debug switch -g was used during the program compile.
__FB_ERR__ Indicates whether error checking switches -e, -ex, -exx was used during program compile.
__FB_DOS__ Indicates a program or section of code that should be compiled for DOS.
__FB_LANG__ Indicates the language compatibility option set during program compile.
__FB_LINUX__ Indicates a program or section of code that should be compiled for Linux.
__FB_MAIN__ Defined in the main startup module.
__FB_MIN_VERSION__( major, minor, patch) Returns 0 if the compiler version is less than the specified version, or -1 if the compiler version is greater than or equal to specified version.
__FB_MT__ Indicates of the multi-threaded switch -mt was used during program compile.
__FB_OPTION_BYVAL__ Returns -1 if parameters to subroutines or functions are passed by value, or 0 if by reference.
__FB_OPTION_DYNAMIC__ Returns 0 if array allocation is set to dynamic, or -1 if set to static.
__FB_OPTION_ESCAPE__ Returns -1 if string literals are processed for escape values when no prefixed with $ or !.
__FB_OPTION_EXPLICIT__ Returns -1 if Option Explicit has been set in the source code, or 0 if not set.
__FB_OPTION_PRIVATE__ Returns -1 if Option Private has been set in the source code, or 0 if not set.
__FB_OUT_DLL__ Returns -1 if the program has been compiled as a shared library (DLL).
__FB_OUT_EXE__ Returns -1 if the program has been compiled as an EXE.
__FB_OUT_LIB__ Returns -1 if the program has been compiled as a library (.a).
__FB_OUT_OBJ__ Returns -1 if the program has been compiled as an object file (.o).
__FB_SIGNATURE__ Returns the signature of the compiler; i.e. FreeBASIC v0.17b.
__FB_VER_MAJOR__ Returns the major version of the compiler.
__FB_VER_MINOR__ Returns the minor version of the compiler.
__FB_VER_PATCH__ Returns the patch version of the compiler.
__FB_VERSION__ Returns the version number of the compiler.
__FB_WIN32__ Indicates a program or section of code that should be compiled for Windows.
__FILE__ Returns the quoted source file name.
__FILE_NQ__ Returns the non-quoted source file name.
__FUNCTION__ Returns the quoted name of the current function block.
__FUNCTION_NQ__ Returns the non-quoted name of the current function block.
__LINE__ Returns the current line number.
__PATH__ Returns the absolute, quoted source file path.
__TIME__ Returns the time the compiler was compiled in 24-hour format.

As you can see there are numerous macros that you can use to gain information on just about every aspect of the compiler and the compiled source code.