Each compiler directive starts from the # character. The following directives are supported:
- #include, #bininclude - file inclusion.
- #if, #then, #else, #elseif, #endif - conditional statements.
- #export, #externally - exporting and importing classes.
- #message, #error, #requires, #orrequires - compilation time information.
- #options - compiler options.
Source File Inclusion
The #include compiler directive is used to include the contents of another file into your program source code during compilation. It has the following syntax:
Pp_dir_include : #include String_literal
The String_literal should specify an existing filename.
#include "pfc\\exception\\exception.ph" % Includes pfc\exception\exception.ph file
#include @"pfc\vpi\vpimessage\vpimessage.ph" % Includes pfc\vpi\vpimessage\vpimessage.ph
This compiler directive uses "include the first file occurrence only" semantics. That is, if a compilation unit contains several include directives for the same file, it will be included only one time with the first include directive.
Each included file must contain several accomplished scopes; an included file cannot contain uncompleted scopes. That is, it should contain several accomplished interface declarations, class declarations, class implementations or/and several compiler directives.
The compiler tries to find the specified include source file in the following way:
- If the filename contains an absolute path, then this file should be included.
- Otherwise, the compiler searches for the specified include filename among the paths that had been defined by the /Include command line option. These paths are handled consequently as they are specified in the option. In the IDE you can set these paths in the Include Directories in the Directories tab of the Project Settings dialog.
If the compiler does not find the specified file a compiling time error is generated.
Binary File Inclusion
The #bininclude compiler directive is used to include (during compilation) the contents of a file (specified by the string_literal string) as a ::binary type constant into your program source code. It has the following syntax:
Pp_dir_bininclude : #bininclude ( String_literal )
This directive can be used in any places where binary constants are allowed. The string_literal should specify an existing filename. The syntax is the same as in the #include compiler directive described in the previous paragraph Source File Inclusions. The compiler tries to find the specified file in the same way as for #include compiler directive.
The typical usage is like this:
constants myBin : binary = #bininclude("Bin.bin"). % Creates value of binary constant from "Bin.bin" file
When creating a binary constant the compiler adds the EOS symbol immediately after this constant, which makes safe the directive usages like this:
constants text : string = uncheckedConvert(string, #bininclude("text.txt")). % Here text.txt is a text file, which is normally not text zero terminated
Exporting and Importing Classes
Compiler directives #export and #externally are used to determine lists of exported and imported classes, respectively. They have the following syntax:
Pp_dir_export : #export ClassNames-comma-sep-list Pp_dir_export : #externally ClassNames-comma-sep-list
These compiler directives are applied only to classes classNames, which do not construct objects.
They can be used only outside scopes; that is, they cannot be used inside declarations of interfaces and classes and they cannot be used inside implementations of classes.
By default, predicates within one executed module are hidden at runtime for all other executed modules. An #export compiler directive makes names of specified classes public outside the module in which they are declared (and implemented). Therefore, all predicates from this module declared in the classes (specified in an #export directive) become accessible while runtime from other executed modules.
Usually, an #export compiler directives can be used in projects, which target modules are DLLs. It enumerates classes declared in a DLL, which should be accessible to other modules that use this DLL.
If a compilation unit export some class, then this compilation unit should contain this class implementation.
Also an #export compiler directives can be used to specify condition expressions for #if compiler directives.
For example, let us suppose that somewhere in the beginning of a compilation unit the compiler has met the #export compiler directive like this:
Then the compiler, if in the subsequent code it meets an #if compiler directive with the same #export compiler directive used as the condition expression, for example like this:
#if #export className #then ... #endif
Then the compiler evaluates the #export condition expression as true and, hence, the compiler executes the #then branch of the conditional compilation directive.
For example, the following #export compiler directive with the subsequent #if conditional compilation compiler directive:
#export className ... #if #export className #then #requires "some.pack" #endif
guaranty that the "some.pack" package will be included into the compilation unit.
From the other hand, if an #export compiler directive is not met by the compiler (somewhere in the compilation unit before the #if compiler directive, which uses the same #export compiler directive as the conditional expression), then the compiler evaluates this #export condition expression as false. Hence, the compiler will not execute the #then branch of the #if conditional compilation directive. That is, the single #if compiler directive without previous #export directive
#if #export className #then #requires "some.pack" #endif
does not requires to include the "some.pack" package.
An #externally compiler directive is counterpart to an #export compiler directive. An #externally compiler directive can be used instead of (and concurrently with) an IMPORTS directive in definition files. An #externally compiler directive enumerates classes, which are declared in a module but implemented in other modules. Therefore, the compiler will not produce errors when it detects such classes. The referenced classes can be implemented (and exported) in DLLs, which can be linked to the module at runtime.
The #export and #externally compiler directives can be used as Condition boolean expressions in Conditional Compilation.
For example, like this:
#if #export className #then #include "Some package.pack" #endif
Compile Time Information
Compiler directives #message, #requires, #orrequires, and #error can be used to issue user-defined messages into a listing file while compilation of project modules and to interrupt compilation.
These directives can be used either outside scopes (interface declaration, class declaration or class implementation), or inside scopes but outside sections. They have the following syntax:
Pp_dir_message: one of #message String_literal #error String_literal #requires String_literal Pp_dir_orrequires-list-opt #orrequires String_literal
When the compiler meets any of these directives, it generates the correspondent warning message and place the directive text into a listing file.
A listing file name can be specified with the compiler directive:
Notice that no empty spaces can be used between the colon : (after /listinglile) and "FileName".
By default the compiler does NOT generate informative messages for the #message, #requires, and #orrequires directives. You can switch generation of these informative messages ON specifying the compiler options:
/listing:message /listing:requires /listing:ALL
#message "Some message"
it will place the following text into the listing file:
C:\Tests\test\test.pro(14,10) : information c062: #message "Some message"
The directive #requires (#orrequires) issues arbitrary user-defined messages about the needed source (object) files into a listing file. The #orrequires directive cannot be used alone: the #requires directive should immediately (separated with white spaces or comments only) precede it.
The directive #error always terminates the compilation and issues the user-defined error message like the following into a listing file:
C:\Tests\test\test.pro(14,10) : error c080: #error "Compilation is interrupted"
You can parse and analyze these messages and accept the required actions. For example, the VDE analyzes information printed by the #requires and #orrequires directives and automatically adds all needed PFC packages and standard libraries to the compiled project (See also the Handling Project Modules topic).
#requires @"\Common\Sources\CommonTypes.pack" #orrequires @"\Common\Lib\CommonTypes.lib" #orrequires @"\Common\Obj\Foreign.obj" #if someClass::debugLevel > 0 #then #requires @"\Sources\Debug\Tools.pack" #orrequires @"\Lib\Debug\Tools.lib" #else #requires @"\Sources\Release\Tools.pack" #orrequires @"\Lib\Release\Tools.lib" #endif #orrequires "SomeLibrary.lib" #requires "SomePackage.pack" #if someClass::debugLevel > 0 #then #orrequires @"\Debug\SomePackage.lib" #else #orrequires @"\Release\SomePackage.lib" #endif
#message "Some text" #if someClass::someConstant > 0 #then #message "someClass::someConstant > 0" #else #message "someClass::someConstant <= 0" #endif class someClass #if ::compiler_version > 600 #then #message "New compiler" constants someConstant = 1. #else #message "Old compiler" constants someConstant = 0. #endif end class someClass
#if someClass::debugLevel > 0 #then #error "Debug version is not yet implemented" #endif
Compiler options directive
Compiler directive #options <string_literal> affects the whole compilation unit. This directive should be used outside scopes and conditional compilation statements in the main source file for a compilation unit (i.e. in the source file which is passed to the compiler). Otherwise the compiler generates a warning message and ignores the directive.
The <string_literal> can only contain the following compiler options:
"/Warning" "/Check" "/NOCheck" "/Optimize" "/DEBug" "/GOAL" "/MAXErrors" "/MAXWarnings"
Otherwise the compiler generates the error message for invalid option. If there are several #options directives they are handled in the textual order.
The conditional programming constructions are part of the Visual Prolog language. Only other compiler directives, Compilation Units, and Program Sections (including empty) can be conditional. The following syntax is used:
ConditionalItem : #if Condition #then CompilationItem-list-opt ElseIfItem-list-opt ElseItem-opt #endif
ElseIfItem : #elseif Condition #then CompilationItem
ElseItem : #else CompilationItem
Here Condition can be any expression, which can be evaluated to fail or succeed during compilation time.
Each one conditional compilation statement must be in one file, that is, the compiler directives #if, #then, #elseif and #else (if present), #endif of the same level of nesting must be in one file.
During compilation the compiler evaluates the conditions, in order to determine which parts to include in the final program. Parts that are excluded from the final program are called the dead branches.
All branches of conditional compilation items are syntax checked and must be syntactically correct. That is, also the dead branches must be syntactically correct.
The compiler only calculates conditions on a need to know basis, i.e. it does not calculate conditions in dead branches.
A condition may not depend on any code, which is located textually inside the conditional statement.
The example below is illegal because the condition depends on the scope (and constant) which is declared inside the condition branch.
#if aaa::x > 7 #then % ERROR! class aaa constants x = 3 end class aaa #else class aaa constants x = 23 end class aaa #endif