The occurence of a wrapped macro symbol in the source code will usually trigger no special actions, no macro expansion is required.
So the macro wrappers shall be constructed first and entered into the preprocessor symbol table, as placeholders for the macro definitions to come. After a subsequent macro definition the existence of an wrapper of the same name can be detected, whereupon the definition is inserted into the wrapper, not into the symbol table. An #undef must be handled in the same way.
The wrapper definitions can be separated from the related source files, e.g. the wrappers for foo.h can be placed into foo.h.wrap. When then a source file is #included, the preprocessor can automagically detect the existence of an associated wrapper file, and can process that wrapper file before the source file.
As outlined above, the definition of a wrapped macro will result in a one-time expansion of the wrapper and macro, in a form that is understandable to the C parser.
Another pragma may be useful to define standard wrappers, so that the translation of a macro definition must not be repeated for every single #define. This pragma can be named #pragma Template.
Here some examples for the intended use of macro wrappers:
will result in
const ACONST = 88;
will result in
const char * TCONST = "version 1.5";
For more complex types the macro name placeholder may be inserted explicitly into the type specification:
#pragma Template __ComplexConst(n) const n = ##;
#pragma Define TCONST __ComplexConst(char *#[dim])
It may be useful do specifiy the types of the wrapper arguments, so that e.g. "type t" can be recognized and treated like an anonymous declarator. But then the type specification must be parsed by the preprocessor, so that the argument name can be inserted in the appropriate place.
#pragma Template __Proc inline void # {##; };
#pragma Template __Func(t) inline t # { return ##; };
#pragma Define foo __Proc((const char *s))
#pragma Define min __Func(int, (int a, int b))
#define foo(s) puts(s)
#define min(a,b) (a) <= (b) ? (a) : (b)
will result in
inline void foo(s) { puts(s); };
inline int min(int a, int b) { return (a) <= (b) ? (a) : (b);
};
Alternatively the old-style procedure declaration syntax can be used, with only argument names in the argument list, and argument declarations being inserted between the procedure declarator and body.
#pragma Template __Func(t, args...) inline t # args { return ##;
};
#pragma Define min __Func(int, int a;, int b;)
resulting in
inline int min(a, b) int a; int b; { return (a) <= (b) ? (a) : (b); };
But then both the Define and the expansion don't look very nice.
Pragma recognition
Treatment of #defines
Wrapper templates
Wrapper arguments and placeholders
Wrapper expansion
Handling of constant expressions
In a first implementation the wrapper templates will be implemented as macros, which are expanded when the wrapper is expanded.
A #pragma "Define" must be recognized, which triggers the usual processing for a #define. The type of the crated macro object must be an appropriate wrapper object (subclass of the macro class). The wrapper object is specially marked in the preprocessor symbol table, for appropriate handling of a subsequent #define of the same name.
The wrapper arguments and placeholders may require modifications of the standard macro methods, both during the definition and expansion of an wrapper. Wrapper arguments may not be subject to a prescan or automatical expansion, the arguments themselves can be wrapped into an expander macro whenever an expansion may be required, e.g. #define _(x) x. During wrapper expansion the wrapper placeholders have a special meaning, perhaps best implemented in virtual methods of the macro/wrapper classes.
Wrapper objects are automatically expanded after a #define of the same name. The macro object, created by the #define, is stored in a field inside the wrapper object.
The handling of constant expressions in the preprocessor and parser may deserve modifications. The preprocessor should access the expanded values of all macros, even when the macros are wrapped. The parser instead should not normally try to evaluate constant expressions, instead expressions should be parsed literally to the compiler, with unexpanded wrapped macros. Unwrapped macros of course are expanded by the preprocessor, it's up to the user to specifiy which macros deserve expansion by the preprocessor, and which macros should be wrapped for handling in the compiler. With regards to the preprocessor, the handling of wrapped macros can be reduced to the mere fact that wrapped macros never must be expanded. All further handling of wrapped macros then can be implemented in the compiler. OTOH the compiler then must know about the macro objects, as created by the preprocessor. This requires at least a specific macro interface for use by the compiler, and then the usability of the already existing macro methods by the compiler is desireable.
A related extension is the definition of Sets, when the target language
supports both enums and sets. In this case all related #defines with values
of 2^n are converted into enum members of ordinality n. Other values must
be translated into set constants of the underlaying enum.