Preprocessor Directives

$DEFINE directive

Purpose:

The $DEFINE preprocessor directive is translated to the C keyword #define.
👉 In the C code translation of $DEFINE, the #define is placed before the #include references. If the #define is needed to be placed after the #include references, then the BCX MACRO directive must be used instead of $DEFINE.

Syntax:

$DEFINE ConstantName Value

Remarks:

Except for its placement in the C translation, $DEFINE is functionally equivalent to the MACRO directive.

Example:

$DEFINE NTDDI_VERSION 0x0A000007

PRINT "NTDDI_VERSION  : " , HEX$(NTDDI_VERSION )

PRINT "WINVER         : " , HEX$(WINVER)

PRINT "_WIN32_WINNT   : " , HEX$(_WIN32_WINNT)

PRINT "_WIN32_IE      : " , HEX$(_WIN32_IE)

Result:

On a Windows 11 x64 machine, the above code, compiled with MSVC, Pelles C, Nuwen, LLVM, and LLVM-MinGW, when run, prints

NTDDI_VERSION  : A000007
WINVER         : A00
_WIN32_WINNT   : A00
_WIN32_IE      : A00

MACRO directive

Purpose:

MACRO is a preprocessor directive which defines an identifier with a string argument. The BCX MACRO keyword is translated to the C keyword #define. In the C code translation, a #define is placed after the #include references. If it is needed for the #define to be placed before the #include references, then the BCX $DEFINE directive must be used instead of MACRO.

MACRO replaces CONST.
👉 The CONST directive keyword has been deprecated and replaced with the BCX keyword MACRO.

Syntax 1:

MACRO ConstantName = Value

Remarks:

👉 Do not append any sigil, (% ! # $), to the ConstantName or to any variable on the right hand side of the MACRO directive.

MACRO ConstantName% = Value%

is invalid and will not compile.
👉 If a MACRO value is used in an $IF, $ELSEIF, or $ELSE directive, it must evaluate as an integer. If a MACRO is defined, but is not given a value, attempting to compile the code will cause a compilation error, as is the case, in the following example.

$BCXVERSION "7.4.0"
 
MACRO NOVALUE

$IF NOVALUE
  PRINT "This will end your dreams"
$ENDIF

Result:

Pelles C compiler throws this error

error #1019: Syntax error in #if/#elif expression.

Microsoft (R) C/C++ Optimizing Compiler Version 19.26.28806 for x86 throws this error

fatal error C1017: invalid integer constant expression

Syntax 2:

MACRO MacroName = (MacroExpression)

Remarks:

👉 The macro and the variable names in macro definitions must be parenthesized.

Example 1:

This example shows the correct parenthesization of a MACRO.

DIM A, B
MACRO CubeAnInteger(C) = ((C) * (C) * (C))
A = 3
B = 1984 / CubeAnInteger(A + 1)
PRINT " Correct parenthesization ! = ", B

Result:

Correct parenthesization ! =  31

Example 2:

This example shows an incorrect parenthesization of a MACRO.

If the parentheses surrounding the right hand expression are omitted, the result will be different from Example 1:, for example,

DIM A, B
MACRO CubeAnInteger(C) = (C) * (C) * (C)
A = 3
B = 1984 / CubeAnInteger(A + 1)
PRINT "Incorrect parenthesization ! = ", B

Result:

Incorrect parenthesization ! =  7936

Example 3:

This example shows an incorrect parenthesization of a MACRO.

And if the parentheses surrounding variable names are omitted, the result again will be different from Example 1:, for example,

DIM A, B
MACRO CubeAnInteger(C) = (C * C * C)
A = 3
B = 1984 / CubeAnInteger(A + 1)
PRINT "Incorrect parenthesization ! = ", B

Result:

Incorrect parenthesization ! =  198

Example 4:

This example shows an incorrect parenthesization of a MACRO.

And again if the parentheses surrounding both the macro and variable names are omitted, the result yet again will be different from Example 1:, for example,

DIM A, B
MACRO CubeAnInteger(C) = C * C * C
A = 3
B = 1984 / CubeAnInteger(A + 1)
PRINT "Incorrect parenthesization ! = ", B

Result:

Incorrect parenthesization ! =  668

Here is an example of using MACRO to create a function-like macro.

MACRO ShowWin(Window) = RedrawWindow(Window,0,0,0),ShowWindow(Window,SW_SHOW)
MACRO HideWin(Window) = RedrawWindow(Window,0,0,0),ShowWindow(Window,0)

PRINT "About to HIDE the console Window for 2 seconds"
DELAY 2

HideWin(CONWIN)

DELAY 2

ShowWin(CONWIN)

PAUSE

Here is another example of MACRO used to create a function-like macro.

MACRO RunEx(lpFile, _
      lpParameters, _
        nShowCmd) = _
    ShellExecute(0, _
            "open", _
            lpFile, _
      lpParameters, _
                 0, _
          nShowCmd)

Remarks:

To use the above macro you must link with SHELL32.LIB. The following shows how RunEx would be called in the application.

RunEx("notepad.exe", "xxx.txt", 1)

Syntax 3:

MACRO ConstName = Expression

Example 5:

Here is a way to use MACRO to embed NULL in a string.

$IPRINT_OFF
 MACRO Filter = "Icons(*.ico)\0*.ICO\0All files(*.*)\0*.*"
$IPRINT_ON

Example 6:

If you have a C code snippet that should look like:

void* F_CALLBACKAPI WaveformCB(void *originalbuffer,
                                    void *newbuffer,
                                         int length,
                                          int param)

it can be translated to BCX like this:

MACRO pF_Callback = (Void*)F_CALLBACKAPI

FUNCTION WaveformCB (originalbuffer AS void PTR, _
                         newbuffer AS void PTR, _
                             length AS INTEGER, _
                              param AS INTEGER) AS pF_Callback

BCX Console Sample Programs using the MACRO directive.

$IF, $IFDEF, $IFNDEF, $ELSE, $ELSEIF, $ENDIF directives

Purpose:

To provide conditional compilation, these five preprocessor directives check whether a controlling conditional expression evaluates to zero or nonzero, and excludes or includes a block of code respectively.

$BCXVERSION "7.4.0"

MACRO FOO = 1
MACRO BAR = 0

$IFDEF FOO
  PRINT "FOO is defined."
$ENDIF

$IF FOO
  PRINT "FOO has a value of ", FOO
$ENDIF

$IFDEF BAR
  PRINT "BAR is defined."
$ENDIF

$IF BAR
  PRINT "BAR has 0 value so this will not print"
$ENDIF

' Note that the "==" equality operator must be used
' when performing a comparison.

$IF BAR == 0
  PRINT "BAR has a value of ", BAR
$ENDIF

$IF FOO == 1
  PRINT "FOO has a value of ", FOO
$ENDIF

$IF FOO == 5
  PRINT "FOO does not have a value of 5 so this will not print", FOO
$ENDIF

$IF FOO OR BAR
  PRINT "FOO has a value of ", FOO
  PRINT "BAR has a value of ", BAR
$ENDIF

Result:

FOO is defined.
FOO has a value of  1
BAR is defined.
BAR has a value of  0
FOO has a value of  1
FOO has a value of  1
BAR has a value of  0

This BCX code

MACRO FooBar
DIM A

$IFNDEF FooBar
A = 1
$ELSE
A = 2
$ENDIF

PRINT A

translates to the following C code, showing that the conditional directives are local in scope located in the main function.

// *************************************************
//            User Defined Constants
// *************************************************

#define FooBar

// *************************************************
//            User Global Variables
// *************************************************

static int     A;

// *************************************************
//                  Main Program
// *************************************************

int main(int argc, char *argv[])
{
#ifndef FooBar
A= 1;
#else
A= 2;
#endif  // Main
printf("% d\n",(int)A);
 return 0;   /* End of main program */
}
👉 Conditional directives cannot encase any of the following:

👉 Any conditional directives will be local in scope to any FUNCTION or SUB procedure including the main function.

👉 If you want global preprocessor conditionals, placed at the top of the file scope level code, executing before anything else, then C style preprocessor code in $HEADER directives must be used as shown below.

$HEADER
#define FooBar
#ifndef FooBar
#define A 1
#else
#define A 2
#endif
$HEADER

PRINT A

Remarks:

A rudimentary ability to conditionally compile BCX code using $IFDEF ... $ENDIF has been enabled.
👉 $IF, $IFNDEF, $ELSE, $ELSEIF, are not supported for this purpose.

The following are example directives and declarators allowed inside SUB and FUNCTION procedures within $IFDEF ... $ENDIF blocks.

 $INCLUDE "a.bas"
 DIM APPLES AS INT
 LOCAL oranges AS LONG
 DIM RAW bananas AS INT
 DIM STATIC spooky AS STRING
 CONST FOO = 1
 MACRO BAR = 2

A GLOBAL variable cannot be created but a GLOBAL variable can be referenced.

Here is a working example:

MACRO USE_NewFoo1
MACRO USE_NewFoo2

CALL TestingX1("TestingX1")
CALL TestingX5("TestingX5")
PAUSE


$IFDEF  USE_NewFoo1
  SUB TestingX1 (x$)
    PRINT x$
  END SUB

  FUNCTION TestingX2 (x$)
    PRINT x$
    FUNCTION = 1
  END FUNCTION

  SUB TestingX3 (x$)
    PRINT x$
  END SUB

  FUNCTION TestingX4 (x$)
    PRINT x$
    FUNCTION = 1
  END FUNCTION
$ENDIF


$IFDEF  USE_NewFoo2
  SUB TestingX5 (x$)
    PRINT x$
  END SUB

  FUNCTION TestingX6 (x$)
    PRINT x$
    FUNCTION = 1
  END FUNCTION

  SUB TestingX7 (x$)
    PRINT x$
  END SUB

  FUNCTION TestingX8 (x$)
    PRINT x$
    FUNCTION = 1
  END FUNCTION
$ENDIF

Result:

TestingX1
TestingX5

Press any key to continue . . .

Example 1:

As an example, suppose you wanted all the messages in your program to be in a particular language, you could use something like the following sample. There would be only one set of language statements in the final .exe

'***********************************************************************
' Conditional compilation using:  $IFDEF/$ELSE/$ELSEIF/$ENDIF directives
'***********************************************************************
'  By uncommenting one of the following CONST statements, the resulting
'  executable code changes as well.  Only the code that is associated
'  with the true condition is compiled, the rest is disregarded.
'*******************************************************************

'CONST ENGLISH
'CONST SPANISH
'CONST GERMAN

$IFDEF ENGLISH
 PRINT "Good Day"
 PRINT "What's happening?"
$ELSEIF SPANISH
 PRINT "Buenas Dias"
 PRINT "Como va?"
$ELSEIF GERMAN
 PRINT "Guten Tag"
 PRINT "Was ist los?"
$ELSE
 PRINT "Greetings Earthling"
 PRINT "Where is the cafeteria?"
$ENDIF

Result:

Greetings Earthling
Where is the cafeteria?

Example 2:

$BCXVERSION "7.5.7"

MACRO APPLES  = 1
MACRO ORANGES = 2

$IF APPLES OR ORANGES '<<-- OR is always Boolean in any IF/ $IF statement 
  PRINT "$IF should translate to (||) and yield 3:", %APPLES OR ORANGES '<<-- otherwise, OR is bitwise 
$ENDIF

$IF APPLES ORELSE ORANGES '<<-- ORELSE is ALWAYS Boolean 
  PRINT "$IF should translate to (||) and yield 1:", %APPLES ORELSE ORANGES ' <<-- ORELSE is ALWAYS Boolean 
$ENDIF

$IF APPLES BOR ORANGES '<<-- This is explicitly bitwise 
  PRINT "$IF should translate to (|)  and yield 3:", %APPLES BOR ORANGES '<<-- This is explicitly bitwise 
$ENDIF

Result:

$IF should translate to (||) and yield 3: 3
$IF should translate to (||) and yield 1: 1
$IF should translate to (|)  and yield 3: 3