-*- Mode:Text -*- Summary of CPP Macro Support This is a summary of the defmacro language extension, its syntax and capabilities, and examples of usage. I have composed this in an attempt to provide a gentler introduction than that available in the man page. Note that the following implementation details are *NOT* standard C++, but rather extensions that we have added via an enhanced, portable preprocessor. This preprocessor has been brought up on Sun OS, SCO XENIX, OS/2, and MVS. There are lots of pieces to the macro facilities. Some were patterned after LISP features such as LOOP and REST: arguments, while others such as the parameterized types support are implementations of design papers published by Stroustrup. Together, they provide significant language features and enhanced power for the programmer. However, it is important to note that once any given macro is expanded, the resulting code is plain-old vanilla C++ syntax acceptable to any C++ translator or compiler. The basic cpp extension mechanism utilized to implement all macro functionality is provided by a #pragma statement. Common macros are defined in this manner. The "defmacro" keyword follows and provides a way to execute arbitrary filter programs (macros expanders) on code fragments. The syntax for defmacro is: #pragma defmacro name options or #pragma defmacro name "file" options or #pragma defmacro name program options where "name" is the name of the filter, and "file" are the name of a source file containing a macro definition, "program" is the name of a program implementing a macro, and options is a series of of optional modifiers for the defmacro explained below. The implementation of a defmacro may be either external to the preprocessor (as in the case of files and programs) or internal to the preprocessor. For example, the template macro implementing parameterized types is internal to the preprocessor to provide a more efficient, performance tuned implementation. Cpp will first search for a file or program in the same search path as that used for include files. If a match is not found, it then searches an internal preprocessor table. If a match is still not found, cpp prints "Error: Cannot open macro file [xxx]", where "xxx" is the name as it appears in the source code. The defmacro syntax also supports a series of options. Zero or more of the following options may be included in any order: recursive - when present, the macro may be recursively expanded. expanding - when present, input to the macro is expanded. delimiter=x - the default delimiter (semicolon) is replaced with 'x'. other - unknown options are passed as arguments to the macro expander. When a defmacro style macro is encountered by the preprocessor, the name and everything until the terminating character -- including all matching and nested levels of {} [] () <> "" '' and comments found along the way -- is piped into the macro procedure's standard-input. The procedure's standard output is scanned by CPP for further processing. The resulting expansion replaces the macro input in the source. The defmacro implementation is used to declare the MACRO (all uppercase) keyword. This is essentially an enhanced #define syntax that supports multiline, arbitrary length, nested macros and cpp-directives, with optional, keyword, and body parameters. To use it include the line: #pragma defmacro MACRO "macro" delimiter=} thus declaring the MACRO keyword whose implementation is a cpp internal routine named "macro". The terminating delimiter for a MACRO is the closing brace character. The syntax for MACRO is: MACRO name (arglist) { body } where "name" is the name of the macro, "arglist" is a list of argument specifiers separated by commas, and "body" is what is substituted on a call to the macro. The "arglist" may specify positional, optional, optional keyword, required keyword, rest and body arguments. When KEY: is specified, the macro takes all following arguments to be keyword arguments that allow the user to specify a particular value. Default values are support by use of an equal sign and value and may be applied to both regular and keyword arguments. The REST: modifier indicates that there is some number of arguments, all of which are referenced by use of the one named identifier. An optional equal sign and identifier will contain the number of arguments remaining. Finally, BODY: indicates that when used, the parameter will be expanded to include all the text within the braces after the macro call. This is useful for identifying a section of code that implements some part of the macro or should be passed to other nested macros. The following examples show some of the power and flexibility of the COOL MACRO capability: MACRO set_val (size, value=NULL, KEY: low = 0, high) { init (size, value, low, high-low) } In this example, "set_val" is the name of the macro that the programmer refers to, "size" is a required positional argument, "val" is an optional positional argument that if not specified in a particular call has a default value of NULL, "low" is an on optional keyword parameter, and "high" is a required keyword parameter. In this example, the macro simple calls the function init() with four arguments. The following show several legal invocations of the macro along with the resulting call to the function init(): set_val (0, high=20) ----> init (0, NULL, 0, 20); set_val (0, low=5, high=15) ----> init (0, NULL, 5, 10); set_val (1, 2, high=25) ----> init (1, 2, 0, 25); See the files "macro-sample.c" and "macro-sample.i" for an example usage of this capability and the corresponding expansion. One of the main uses of the defmacro facility is the implementation of templates to support parameterized types. The syntax of the template grammar is that as specified by Stroustrup in his paper "Parameterized types for C++" in the 1988 USENIX C++ Conference Proceedings. Cpp implements this functionality such that there will be minimal source code conversion necessary when this feature is finally implemented in the C++ language. A class programmer who wishes to implement a vector class object wants to make design and implementation decisions in such a way as to facilitate a simple and consistent interface for the application programmer, no matter what type of thing is to be stored in the vector. In addition, replication of code for each specific type should be avoided if at all possible. To accomplish this, the class programmer implements a vector template, with the type selection left to the application programmer. The following example is for a Vector header file: template class Vector { // Parameterized Vector class private: Type* v; // Data of type pointer to Type int size; // Size of vector object public: Vector (); // Empty constructor Vector (int); // Constructor with size Vector (Vector&); // Constructor with reference ~Vector (); // Destructor Type& operator[](int n); // Operator[] overload for Type Type& element (int n); // Return element of type Type ... // ... other methods ... }; An application programmer would include this header file in the appropriate source file. In addition, common header file must be included to provide the #pragma statement declaring the defmacro template shown below. This must occur before the inclusion of the Vector header file and so is done within Vector.h if it has not already been included. #pragma defmacro template "template" delimiter=} An application programmer also places the following lines in his/her source code to use the parameterized Vector class for a specific type: DECLARE Vector; IMPLEMENT Vector; Vector vs(30); The DECLARE macro implements a series of #define preprocessor statements that will declare and define code for a Vector of doubles. Together with the template macro expander. A class definition with its associated methods is created ready to be compiled, all performed invisibly to the user. The common header file must include the #pragma statements that allow DECLARE and IMPLEMENT to work also. They are as follows: #pragma defmacro MACRO "macro" delimiter=} recursive #pragma defmacro DECLARE "declare" delimiter=> recursive lines #pragma defmacro IMPLEMENT "implement" delimiter=> recursive lines See the files "template-sample.c" and "template-sample.i" for an example usage of this capability and the corresponding expansion. There are several other internal macros included with cpp that we have experimented with. These can safely be ignored. For the interested parties who want to delve more we refer you to the comments in the source files.