The front end Xcpp includes a powerful preprocessor which is applied to the source code before subsequent compilation by a standard C/C++ compiler.
The Xc++ preprocessor directives begin with the @ character.
To illustrate the preprocessor self contained code examples are shown before and after translation. Red syntax colouring is used for the directives and blue for the standard C++ keywords.
Each directive has a defined translation - i.e. what it becomes in the result of applying the preprocessor.
Macros can be regarded as variables at preprocessor time.
Macros can be defined recursively.
The grammar of the preprocessor language is defined in EBNF.
Directive | Description |
---|---|
@def | Macro definition |
@nakeddef | Macro definition where no local namespace is pushed onto the namespace stack when the macro is invoked |
Write to stdout during compilation | |
@assert | Assertion of a given expression at pre-processor time |
@assertfails | Verifies that macro expansion of its argument generates a macro expansion error. Used to unit test the xcpp preprocessor |
@fail | Aborts the xcpp preprocessor displaying an error message |
@runpython | Macro expands its parameter then runs it under the python interpreter |
@defpython | Defines a macro in terms of a python expression |
@$ | $variable definitions |
@@ | Expands into nothing, acts as a delimiter for the lexical scanner |
@let | Redefines the value of a macro to allow macros to be used like variables at preprocessor time. |
Directive | Description |
---|---|
@if-@else | Conditional macro expansion |
@scope | Defines a local scope for macro definitions, for limiting the scope of macros, to avoid accidental invocation outside their intended usage |
@str | Expands into a double quoted string obtained by first macro expanding its argument then converting it to a double quoted string |
@strx | Same as @str except that linefeeds cause the string to be broken up into separate strings |
@unstr | Removes the quotes on the macro expansion of its string parameter |
@[] | Outputs the result of macro expanding the result of macro expanding its parameter |
@quote | Macro expands literally to its argument (i.e. without macro expanding it) |
@() | Outputs the evaluation of the expression which is the macro expansion of its parameter |
@import | Translates to a #include directive |
@relativepath | Calculates a relative path between two given absolute paths |
@while | Allows for loops |
@for | Allows for simple loops. The body of the directive is repeately macro expanded for different values of the loop variable(s), taken in sequence from the iteration list which is a comma separate list of values enclosed in square brackets. |
There are many different error conditions that can cause the xcpp preprocessor to abort with some failure indication. Error messages are displayed in a similar form to the Microsoft Visual C++ compiler.
The directives @assert, @assertfails and @fail are specifically aimed at aborting the xcpp preprocessor.
Sometimes a macro has bound to some text, and we need to put it in double quotes so it looks like a C/C++ string literal. At other times it can be useful to remove the quotes. @str and @strx allow for adding the quotes, and @unstr allows for removing them.
Consider a procedural execution model of the xcpp preprocessor. A file is typically translated by processing its text from start to finish. Typically text is processed by copying it verbatim from input to output.
C/C++ comments are copied verbatim without further processing. It follows that @def directives can be commented out easily. E.g
Before translation | After translation |
---|---|
|
|
Note that the comment appeared in the output. To strip a comment from the output, precede the C/C++ comment with @. i.e. use @//... or @/*...*/. For example:
Before translation | After translation |
---|---|
|
|
The xcpp preprocessor uses a stack of namespaces to record macro definitions. Macro names are looked up by searching each namespace in turn, starting from the top of the stack. Therefore names in an inner scope hide names in an outer scope. When translation first begins at the top of the file, the stack is initialised with a single entry, which is the global namespace for macro definitions. Note therefore that macros at the outermost scope are recorded in the global namespace.
When a @def directive is processed the macro is recorded in the namespace at the top of the stack. The body of a macro is skipped over without being processed. Therefore, at this time the nested @def directives are ignored. Processing the body of a macro only occurs when the macro is invoked (if ever). When a macro is invoked a new namespace is pushed onto the top of the stack in preparation for processing the body of the macro. It is popped when the processing of the body is completed. The effect is that the execution of the macro creates a local namespace for nested @def directives.
A macro is expanded in the context of the stack of namespaces defined at the point of invocation, not the point of definition. In the following example, m is invoked in a context where n = 2. The fact that n = 1 at the point of definition of m is irrelevant.
Before translation | After translation |
---|---|
|
|
This concept of expanding a macro in the context of the caller (and not the callee) makes it possible to write very flexible macros without the need to explicitly parameterise with large numbers of formal arguments.
Most directives introduce private namespaces for local macros. For example, local macros can be defined in either the boolean condition or the body of an @if directive, or within a @(...) directive:
Before translation | After translation |
---|---|
|
|
The directives @runpython and @defpython provide access to a Python interpreter. The intention is for complex macros to be defined using Python, which represents a well supported and well documented language, allowing the xcpp preprocessor to be simpler.
The directives @@, @[] and @quote can be used to prevent or force macro expansion.
The xcpp preprocessor allows for evaluation of expressions in a similar manner to the C/C++ compiler.