To use the preprocessor

The compiler bic6.2 for 3GL program scripts has a built in preprocessor which has these functions:

  • Include files
  • Define macros
  • Variable Macro Arguments
  • 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.

Include Files

To include files this 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 this entry occurs in $BSE/lib/fd.6.2<package_comb>:

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.

Macro Definition

To define macros this 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, as in this 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 this construction:

#ifdef INCR        | without arguments 
#undef INCR(i)
#endif |  of #ifdef / #endif calls 

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.

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 this 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 this 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 these 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.

Pragma Codes

Pragma codes are a kind of compiler options. These 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 this 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 this pragma on top of the source:

#pragma nowarnings

After a number of warnings, you can enter this pragma:

#pragma warnings

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 such as +, -, *, /, =, <, >, <=, >=, <>, 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 as in these 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 

The preprocessor only works during compilation of a 3GL source because the standard generator std_gen6.2 does not have a preprocessor pass. You cannot use 4GL events in a #if, #ifdef or #ifndef.

You cannot use a #ifdef statement in an embedded SQL query. This formula is not allowed:

select * 
#ifdef STANDARD 
    ...
		  from x 
#else 
    ... 
    from y 
#endif 
    where ... 
selectdo 
    ... 
endselect

But this 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__)