Operator & Foundation
Macro operator (unary): compiles character expression at runtime.
The macro operator compiles character strings at runtime and executes the result. When the macro operator is found in a character string or in text, parts of the character string can be replaced with the contents of one or more variables.
The character string, which the macro operator compiles at runtime, is permitted to contain only expressions and not commands or statements. Also, all names or symbols for functions or variables which are referenced within the character string must exist at runtime. A character string containing names for LOCAL or STATIC variables cannot be compiled by the macro operator. This is because the corresponding symbol references are resolved at compile time and no longer exist at runtime.
The length of a character string which can be compiled and executed by the macro operator is not limited by Xbase++.
A character string can contain the names (or symbols) of PRIVATE or PUBLIC variables preceded by a macro operator. A period can follow the variable name in order to separate the symbol from the remaining text. In a character string such as this, the variable references in the character string are replaced at runtime with the contents of the appropriate variable. A substitution occurs only when the contents of the stored variable is also a character string. An example of text substitution is provided in the following code:
The variables used in a text substitution must be PRIVATE or PUBLIC. If the symbol for the variable does not exist at runtime, a runtime error is generated.
Compiling at runtime
When the macro operator precedes a variable of character data type or when it precedes a character expression in parentheses, the character string is compiled, the code executed, and the result is returned. The macro operator compiles the contents of a character variable or the result of a character expression at runtime of the program. The following code is an example:
When the character string being compiled contains references to functions or variables, the names (or symbols) must be visible at runtime. Referenced functions may not be declared with STATIC FUNCTION. Functions must be linked into the program. If a function is called only via the macro operator (its function name therefore only appears in character strings and nowhere else in the program) the function must be declared with the statement REQUEST so that it is linked into the executable file.
If variables are referenced in the character string, they must be of storage class PRIVATE or PUBLIC. The names for LOCAL and STATIC variables do not exist at runtime and therefore cannot be compiled by the macro operator. The value of a PRIVATE or PUBLIC variable can be of any data type. This means that a character string compiled by the macro operator at runtime can access arrays or objects if they are referenced in a PRIVATE or PUBLIC variable. Example:
The result returned by the macro operator is the value of the last expression in the compiled character string. When &cMacro in the preceding example is executed, it results in two lines of output. The first line contains the values specified as the QOut() parameters. The second line of output is the return value of the macro execution displayed in response to the command ?. Since the return value of QOut() is NIL, the return value output by ? is NIL.
Generating variables at runtime
The macro operator compiles a character string at runtime, executes the result and returns the result of the last expression in the character string. Using the macro operator, it is possible to generate variables dynamically at runtime, whose names (or symbols) are not known by the application at compile time. This feature is demonstrated in the following sample:
An example of how this could be valuable would be in the creation of a command line interpreter with Xbase++.
Generating code blocks at runtime
Since the macro operator returns the result of a compiled character string it can be used to generate code blocks at runtime. This, along with the possibility that code blocks can be stored in files, means that Xbase++ offers a powerful tool for programming "data-driven" applications, where the user defines data or expressions that drive the operation of the program.
A code block is generated when a character string containing the syntax for a code block is compiled by the macro operator at runtime of a program. In this case the result of the macro operator is a code block which can be executed with the function Eval(). Example:
In this case, a code block offers significant speed advantages over the macro operator. The code block is compiled only once and the resulting executable code can be assigned to a variable. When a variable contains a code block, the corresponding code can be executed directly by the function Eval(). Should the variable instead contain a character string, the code in the character string must be compiled with the macro operator each time before it can be executed.
Sending messages to objects
The macro operator makes possible a technique by which the message sent to an object can change at runtime. To implement the technique, the message must be coded as a character variable prefaced by the macro operator. The macro operator compiles the contents of the variable at runtime, and passes the result as a message to the object using the send operator. The following example illustrates this:
In the example the command @...GET is used only to create a Get object which is stored in the last element of the GetList array. The object is assigned to a LOCAL variable oGet and finally the value of the instance variable :row is displayed using the macro operator.
Nested macro operations
The macro operator can be deeply nested. An example of nesting macros is shown in the following code:
The macro operator in commands
The macro operator cannot interpret any Xbase++ commands. This is because commands are translated by the preprocessor into function calls. However, the macro operator can be used in command arguments. An example is given in the following sample:
The command USE is shown in three different forms in this example. The file name can be passed to the command as a literal or as a character expression in parentheses. When the file name is a variable preceded by the macro operator, the preprocessor removes the macro and treats the argument as an extended expression.
If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.