A macro can be defined and then referenced any number of times further down in the translation unit:
Before translation | After translation |
---|---|
|
|
We say that macro m has been invoked twice in the above example.
Often a macro is defined in a header file so it can be invoked
from multiple source files. We refer to the substitution string 100
as the body of m.
The translation maintains existing indentation and comments. Macro definitions are stripped away, and all invocations of a macro after the point of definition result in substitution by the body.
In similar fashion to C/C++ #define macro definitions, xcpp macros can take any number of formal arguments. By default the macro expander performs substitution without regard for the semantics of C/C++ programs. The following example shows how inappropriate usage can lead to surprises because of a lack of bracketing of expressions:
Before translation | After translation |
---|---|
|
|
The return type of a macro can be specified. In the following example it is stipulated that add(x,y) returns an int. This causes xcpp to perform calculations at compile time in order to coerce the return value.
Before translation | After translation |
---|---|
|
|
This can have a number of advantages:
The types of the formal arguments of a macro can optionally be specified. This leads to eager evaluation of the arguments by the xcpp preprocessor at the point of invocation. The advantages described above for strong typed return values also apply to strongly typed formal arguments. Here is an example:
Before translation | After translation |
---|---|
|
|
The following example illustrates strong typing of both the return value and arguments of a macro:
Before translation | After translation |
---|---|
|
|
In the directive @def x = y, we call y the body. The body is delimited in a number of different ways. In the following example, macros named w1,w2 and w3 are all equivalent:
Before translation | After translation |
---|---|
|
|
The extent of the body is determined before considering its macro expansion. The xcpp preprocessor uses the lexical scanner to help determine the extent of the body. This allows it to ignore braces inside // or /*...*/ comments or in single or double quoted strings. From a position just after the ’=’, it scans past space and tab characters on that line. If the next character is not a linefeed or left brace then it sets the body to be all the remaining characters on that line (not including the linefeed). Otherwise the body is assumed to be an indented block of text delimited (non-inclusively) by braces. Nested braces are allowed within the block, as long as braces pair up correctly (xcpp counts braces to determine the end of the block). The body doesn’t include the final linefeed - i.e. on the last line immediately before the final right brace.
In rare circumstances when there’s a need to workaround the counting of braces, @unstr can be used (this directive is described in section 6.3). For example:
Before translation | After translation |
---|---|
|
|
The xcpp preprocessor applies an indentation to an entire block of text under macro substitution. The effect is that the output of the preprocessor is often conveniently formatted. For example:
Before translation | After translation |
---|---|
|
|
Macro definitions can be nested. The following example shows how macro m defines a local macro called y. Local macros are not accessible outside the scope of the containing macro.
Before translation | After translation |
---|---|
|
|
An argument to a macro can be back-quoted in order to ensure it is parsed as a single indivisible argument to bind to the formal argument of the macro. For example, it is necessary to back-quote the argument map<K,V> because it contains commas, and the macro expander doesn’t count angled brackets to delimit terms when a macro is invoked.
Before translation | After translation |
---|---|
|
|
Macros have a scope in which they are available to be invoked. Macros at file scope are only local to the file in which they appear. For example if a header file defines a macro w like this
@def w = 1
then w is only visible within that header file, and not to other files than import that header
To allow a macro to be visible in other files it needs to be exported using the '+' qualifier. A macro can be conditionally exported from a nested scope. For example the following code exports macros named x and y (but not z).
@def+ x = 2
@if (true)
{
@def+ y = 3
}
@if (false)
{
@def+ z = 4
}