#define
Topic group Related topics Example
Defines an identifier (name) for use in controlling program compilation, defining constants, or creating macro-functions.
Syntax
#define <identifier> [<replacement text>]
#define <identifier>(<parameter list>) <replacement text with parameters>
<identifier>
A name. It identifies the text to replace if <replacement text> is supplied. The name must start with an alphabetic character and can contain any combination of alphabetic or numeric characters, uppercase or lowercase letters. The identifier is not case-sensitive.
(<parameter list>)
Parameter names that correspond to arguments passed to a macro-function that you create with #define <identifier>(<parameter list>) <replacement text>. If you specify multiple parameters, separate each with a comma. There cannot be any spaces between the <identifier> and the opening parenthesis of (<parameter list>), or after any of the parameter names in the <parameter list>.
<replacement text>
The text you want to use to replace all occurrences of <identifier>. If you specify <replacement text>, the preprocessor scans each source code line for identifiers and replaces each one it encounters with the specified replacement text. <replacement text> can be any text that is part of a dBL program, such as a string, numeric expression, or series of commands.
Description
The #define directive defines an identifier and optionally lets you replace text in a program before compilation. Each #define definition must begin on a new line and is limited to 4096 characters.
Identifiers are available only to the program in which they are defined. To define identifiers for use in multiple programs, place them in a separate file and use #include to include that file as needed.
You must define an identifier in a file with the #define directive before you can use it. Once it has been defined, you cannot #define it again; you must undefine it first with the #undef preprocessor directive.
Use the #define directive for the following purposes:
To declare an identifier and assign replacement text to represent a constant value or a complex expression.
To create a macro-function.
To declare an identifier with no replacement text, so you can use it with the #ifdef or #ifndef directive.
To declare an identifier with replacement text, so you can use it with the #if directive.
The effect of #define is similar to a word processor’s search-and-replace feature. When the preprocessor encounters a #define identifier in the text of a script, it replaces that identifier with the <replacement text>. If there is no <replacement text> for that identifier, the identifier is simply removed. For example:
#define test 4 // Create identifier with value
? test - 3 // ( 4 - 3 ) = 1
#undef test // #undef to change definition
#define test // Create identifier with no value
? test - 3 // ( - 3 ) = -3
Because the preprocessor runs before a program is compiled and performs simple text substitution, the use of #define statements can in effect override memory variables, built-in commands and functions, and any other element having the same name as <identifier>. This is shown in the following examples.
// Overiding a variable
somevar = 25; // Creates variable
#define somevar 10; // Until further notice, "somevar" will be replaced
? somevar // Compiles argument as "10". Displays 10
#undef somevar // "somevar" no longer replaced
? somevar // Displays 25
// Overriding a function
#define cos(x) (42 + x) // Function adds 42
? cos(3) // Compiles argument as "(42 + 3)". Displays 45
To use #define directives in WFM and REP files generated by the Form and Report designers, place the directives in the Header section of the file so that the definitions will not be erased by the designer.
Declaring identifiers to represent constants
Assign an identifier to represent a constant value or expression when you want the preprocessor to search for and replace all instances of the identifier with the specified value or expression before compilation. When used in this manner, the identifier is known as a manifest constant. It’s common practice to make the name of the manifest constant all uppercase, with underscores between words so that it stands out in code. For example:
#define SECS_PER_HOUR 3600 // Number of seconds per hour
#define MSECS_PER_DAY (1000*24*SECS_PER_HOUR) // Number of milliseconds per day
Note that when using a manifest constant to represent a numeric expression, you should place the entire expression inside parentheses. This prevents possible errors due to the precedence of operators used to evaluate expressions. For example, consider the following calculation:
nDays = nTimeElapsed / MSECS_PER_DAY
Without parentheses, this statement would compile as:
nDays = nTimeElapsed / 1000*24*3600
That’s incorrect—it divides by 1000 then multiplies by 24 and 3600. (The multiplication and division operators are at the same level of precedence, so the expression is evaluated from left to right.). By placing parentheses around the manifest constant definition as shown, the statement would compile as:
nDays = nTimeElapsed / (1000*24*3600)
Because of the parentheses, the expression is evaluated correctly: the value of the constant is evaluated first, then used as the divisor.
Manifest constants streamline your code and improve its readability because you can use a single identifier to represent a frequently used constant or a complex expression. In addition, if you need to change the value of a constant in your program, you need to change only the constant definition and not every occurrence of the constant.
To replace an identifier only in parts of a program, insert #undef <identifier> into your program where you want the search-and-replace process to stop.
Creating macro-functions
When the preprocessor encounters a function call that matches a defined macro-function, it replaces the function call with the replacement text, inserting the arguments of the function call into the replacement text. This is shown in the following example.
#define avg(num1,num2) (((num1)+(num2))/2) // Average two numbers
...
n1=20
n2=40
? avg( n1, n2 ) // Displays 30
The arguments in the macro-function call are substituted exactly as they are shown in the macro-function definition. In this example, the last statement compiles as:
? (((n1)+(n2))/2)
When using a macro-function to perform calculations, always use parentheses to enclose each parameter and the entire expression in the macro-function definition as shown. If you leave them out, errors may result due to the precedence of operators, as shown in these (somewhat contrived) examples:
#define avg(num1,num2) (((num1)+(num2))/2)
#define badAvg(num1,num2) (num1+num2)/2
? 12 / avg( 2, 4 ) // 12/(6/2) --> displays 4
? 12 / badavg( 2, 4 ) // 12/6/2 --> displays 1
Unlike functions in dBL, the number of arguments passed from a macro-function call must match the number of parameters defined in your #define statement.
Declaring identifiers for conditional compilation
In addition to using identifiers for constants and macro-functions in dBL code, they are used for conditional compilation with the #if, #ifdef, and #ifndef directives.
Defining an identifier without replacement text lets you use it with the #ifdef or #ifndef directive to test if the identifier exists. When used in this manner, the existence of the identifier acts as a logical flag to either include or exclude code for compilation.
When an identifier is defined with replacement text, you can use comparison operators to check the value of the identifier in an #if directive, and conditionally compile code based on the result. You can also use #ifdef and #ifndef to test for the existence of the identifier.
Nesting preprocessor identifiers
You can nest preprocessor identifiers; that is, the replacement text for one identifier may contain other identifiers, as long as those identifiers are already defined, as shown in the following example:
#define SECS_PER_HOUR 3600 // Number of seconds per hour
#define MSECS_PER_DAY (1000*24*SECS_PER_HOUR) // Number of milliseconds per day
You cannot use the identifier being defined in its replacement text, however.