| # To use the preprocessor The compiler bic6.2 for 3GL program scripts has a built in preprocessor which has the following functions: Include files Define macros 1 Variable Macro Arguments 2 Token Pasting Set an identification in the object Pragma codes Conditional compiling All preprocessor directives starts with a #. A preprocessor directive must be the first word on a source line, possibly with leading spaces or tabs. 1. Include Files To include files the following statement is used: #include filename or #include <filename> Files included between < and > are searched in the $BSE/include<rel.number> directory. This directory is reserved for system headers and cannot be used for applications. Normally the filename between quotes is searched by using the standard file redirection method. Suppose the following entry in $BSE/lib/fd.6.2<package_comb> occurs: `ippmmm:/usr/bse/standard6.2` and there is an include statement like: #include ippmmmheader The file /usr/bse/standard6.2/ippmmm/immmheader0 is included. If a / occurs in the filename, the file is searched using the specified path name. When a file is included twice, the second include is ignored. Note that the preprocessor only works during compilation of a 3GL source because the standard generator std_gen6.2 does not have a preprocessor pass. So you cannot use 4GL events in an included file. 2. Macro Definition To define macros the following statement can be used: #define macroname[(arguments)] macro definition macro definition To undefine macros the following statement can be used: `#undef macroname[(arguments)]` The macro names in the source are expanded to the macro definition. If a macro definition does not fit on one line, you can continue the definition on the next line(s) using the caret symbol ^ at the beginning of the next line. You can use arguments in the macro. Example:
    #define a(x, y, z) 	     for i = x to y
    ^                              z()
    ^                        and for | The definition
    a(1, 100, func)                  | The invocation
Macro definitions with the same name but with different number of arguments are different, like the following example:
    #define x					        definition without arguments
    #define x()					      definition with 0 arguments
    #define x(arg1)       definition with 1 argument arg1
    #define x(arg1,arg2)  definition with 2 arguments
    #define x(...)        definition with ellipsis ... (see below)
Note that there is a difference between a macro without arguments x and a macro with zero arguments x(). In the first case you must use x in the program script and in the second case you must use x() in the program script to call the macro. The #undef statement causes the macro definition to be forgotten. The number of arguments in the #undef call must match the number of arguments in the definition.
     #define MAXLENGTH			 1000
    #define INCR 1
    #define INCR(i) i=i+INCR
    .....
    #undef MAXLENGTH
    #define			 INCR 2 | Redefines INCR
    .....
    #undef INCR(i)
If you apply #undef to an unknown macro, an error occurs. To be sure that the macro is defined, use the following construction:
    #ifdef INCR        | without arguments
    #undef INCR(i)
    #endif |  of #ifdef / #endif calls
see 5. Conditional Compiling for a detailed description 2.1. Variable Macro Arguments To define a macro, you can also use the ellipsis notation ( , ... ) to give the macro a varying number of arguments. Example:
    #define  fillbuf(buf1, format, ...) buf1 = sprintf$(format,...)
    | macro call string buffer(100) long l_val double d_val fillbuf(buffer,
     %d. %s = %d %5.2f, 1, Value, l_val, d_val)
The macro definition can contain a number of arguments, but the ellipsis notation must be the last argument. This notation can be used with functions that also use the ellipsis notation, such as the `sprintf()`, `message()` and `mess()` functions. 2.2. Token Pasting In the macro you can enter a part of a variable as macro argument. The symbol ## is used to distinguish macro arguments and the rest of the macro, but is omitted in the macro call. You can paste more macro arguments together as one identifier. This principle is called token pasting. Example
    #define p(x) 			   message("%d", var##x)
    #define q(x,y)     message("%d", x##y)
    #define r(x,y,z)		 message("%d", x##y##z)

    long var1a, var1b

    p(1a)          | becomes: message("%d", var1a)
    p(1b)          | becomes: message("%d", var1b)
    q(var, 1a)     | becomes: message("%d", var1a)
    q(var, 1b)     | becomes: message("%d", var1b)
    r(var, 1, a)   | becomes: message("%d", var1a)
    r(var, 1, b)   | becomes: message("%d", var1b)
This method is used to reuse parts of source code, where using functions is impossible or difficult to use functions. See also the following example. Example
    #define VRC(table,v,r,c)
    ^                  tt##table##.vers = v
    ^                  tt##table##.rele = r
    ^               	  tt##table##.cust = c

    VRC(adv100, "6.2", "a", "")
    VRC(adv200, "6.2", "a", "")
    VRC(adv300, "6.2", "a", "")
Object Identifications To set an identification in the object, use the following statement: `#ident "@(#)Identification of object"` Example `#ident "@(#)<progname>, YY/MM/DD [HH/MM], version, author"` A default identification is always placed in the object by the compiler with the following contents:
     #ident "@(#)<source name>, YY/MM/DD, [HH/MM],
                              From ${logname}"
The UNIX what command writes all object lines beginning with '@(#)' on standard output. Besides this default identification the programmer can use his own identification which is also placed in the object. 4. Pragma Codes Pragma codes are a kind of compiler options. The following pragma codes can be used:
#pragma nodebug | Do not show the source while debugging.
#pragma debug | Show the source while debugging.
#pragma nowarnings | Do not give warnings about the source.
#pragma warnings | Give warnings about the source.
#pragma notransactions | The source only contains read actions. There are no transactions in the source. So, it is sufficient to start one database server.
#pragma warning <text> | The programmer generates his own warning. (Warning level 15). See example.
#pragma fatal <text> | The programmer generates his own error.
#pragma sticky | Do not remove the object out of memory when the process ends.
#pragma used <component> <code> | See below.
Example:
    #pragma warning This is not a fine solution !

    | After compilation the following warning appears:
    | <Source(line)>: Warning(15): This is not a fine solution !
In some cases the where-used list will not be updated automatically. For example, when a session code is entered but not expected. For example, `message("ttadv2130s000") or bms.send("command", event, "ttaad3100s000", pr.id)` To put this session code into the where-used list, enter the following command line: `#pragma used session ttadv2130s000` Following is an overall picture of the pragma codes to update the where-used list:
#pragma used include | <file>
#pragma used table | <table code>
#pragma used field | <field code>
#pragma used domain | <domain code>
#pragma used message | <message code>
#pragma used question | <question code>
#pragma used session | <session code>
#pragma used menu | <menu code>
#pragma used dll | <dll objectname>
#pragma used chart | <chart code>
Usually the where-used list will be updated. In case of functions, the where-used list is updated if the function call contains the string value. (see example) Example
    mess("ttadvs0000", 1)  | Where-used list will be updated

    str = "ttadvs0000"
    mess(str, 1)           | Where-used list will not be updated

    #pragma used message ttadvs0000
Pragma codes can be placed everywhere in the source. For instance, you have set the following pragma on top of the source: `#pragma nowarnings` After a number of warnings, you can enter the following pragma: `#pragma warnings` 5. Conditional Compiling
    #ifdef <macro>
    			 The source after #ifdef up to #else/#elif/#endif will be compiled if
    			 <macro> is defined. Otherwise this source is ignored.

    #ifndef <macro>
        The source after #ifndef up to #else/#elif/#endif will be
    			 compiled if <macro> is not defined. Otherwise this source is ignored.

    #if <constant expression>
        The source after #if up to #else/#elif/#endif will
    			 be compiled if <constant expression> is TRUE. Otherwise this source is
    			 ignored.

    #else
        If the condition belonging to #if/#ifdef/#ifndef/#elif is FALSE,
    			 the source after #else up to #endif will be compiled. If the condition
    			 belonging to #if/#ifdef/#ifndef/#elif is TRUE, the source after #else up to
    			 #endif will be ignored.

    #elif <constant expression>
        #elif is a
    			 combination of #else and #if.

    Example
    #if <constant expression>
         ...
    #else
    			 #if <constant expression>
         ...
    #else
         ...
    #endif
    #endif

    Is equal to:
    #if
    			 <constant expression>
         ...
    #elif <constant expression>
         ...
    #else
         ...
    			 #endif

    #endif
        To finish a part of the source started with #ifdef/#ifndef/#if

    #undef <macro>
        To delete a macro definition, which means that this macro
    			 is not known on a next #ifdef call.
The <constant expression> in a #if, #elif is a numeric expression. In this expression you can only use macros of long type and operators like +, -, *, /, =, <, >, <=, >=, <>, and, or, not, ?:, (). You can use nested #if structures. You can define a macro while starting the compiler by using the -D option.
        bic6.2 -D<macro>     | no value means default 1
        bic6.2 -D<macro>=<value>
    			 bic6.2 -D<macro>='any token string'
These macros can also be used in the #if conditions like the following examples:
     bic6.2 -DDEBUG -DMYTEST <source>

    |source
    #if DEBUG and MYTEST
           message(Some debug information)
           ...
    #endif
    bic6.2 -DCUSTOMER_X -DCUSTOMER_Y <source>
    bic6.2 -DSTANDARD <source>

    #if STANDARD
           ....
    #elif ( CUSTOMER_X and (not CUSTOMER_Y) )
           ....
    #endif
You can inactivate a file with #ifdef: Example:
    #ifdef OLD
    ....
    ....
    ....
    #endif
Note that the preprocessor only works during compilation of a 3GL source because the standard generator std_gen6.2 does not have a preprocessor pass. So you cannot use 4GL events in a #if, #ifdef or #ifndef. You cannot use a #ifdef statement in an embedded SQL query. So,
    select *
    #ifdef STANDARD
        ...
    		  from x
    #else
        ...
        from y
    #endif
        where ...
    selectdo
        ...
    endselect
is not allowed, but the following construction is possible:
    #ifdef STANDARD
    select * from x where ...
    selectdo
    ...
    endselect
    #else select * from y where ...
    selectdo
    ...
    endselect
    #endif
Two keywords are implemented to give debug information in 3GL sources:
    __FILE__     : contains the name of the current source
    __LINE__     : contains the line number of the current source
Example
    message(This is at line %d in the source %s, __LINE__, __FILE__)