How can one define a macro that operates on the arguments passed to another macro? Specifically, consider the following:
#define PRINT(a) printf(#a": %d", a) #define PRINT_ALL(...) ? ? ? THE PROBLEM ? ? ?
Desired Usage:
int a = 1, b = 3, d = 0; PRINT_ALL(a,b,d);
Recursive macros are possible in C using an unconventional workaround. The goal is to create a MAP macro that resembles:
#define PRINT(a) printf(#a": %d", a) MAP(PRINT, a, b, c) /* Apply PRINT to a, b, and c */
We begin by creating a way to output something resembling a macro call that isn't evaluated:
#define MAP_OUT
Considering the following macros:
#define A(x) x B MAP_OUT (x) #define B(x) x A MAP_OUT (x)
Evaluating A(blah) produces the text:
blah B (blah)
The preprocessor doesn't detect recursion here, as the B (blah) call is merely text at this stage. Feeding this text back into the preprocessor expands it:
blah blah A (blah)
Evaluating this output again expands the A (blah) macro, completing the recursion. This process continues until the output is fed back into the preprocessor again.
To facilitate multiple evaluations, we use the EVAL macro to pass arguments down a chain of macro calls:
#define EVAL0(...) __VA_ARGS__ #define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__))) #define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__))) #define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__))) #define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__))) #define EVAL(...) EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))
Each level amplifies the effort of its predecessor, ultimately evaluating the input 365 times. For example, EVAL(A(blah)) yields 365 copies of blah followed by an unevaluated B (blah). This setup provides the fundamentals for recursion within certain stack depth limitations.
The next hurdle is determining when to terminate the recursion.
We define a MAP_END macro as the end-of-list marker:
#define MAP_END(...)
Evaluating this macro does nothing, signaling the end of recursion.
To choose between the two macros, we use MAP_NEXT, which compares a list item with the special end-of-list marker ():
#define MAP_GET_END() 0, MAP_END #define MAP_NEXT0(item, next, ...) next MAP_OUT #define MAP_NEXT1(item, next) MAP_NEXT0 (item, next, 0) #define MAP_NEXT(item, next) MAP_NEXT1 (MAP_GET_END item, next)
MAP_NEXT determines whether to continue or stop based on the list item and the next item. It returns MAP_END if they match or the next parameter otherwise.
With these components, we can construct usable versions of A and B:
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__) #define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)
These macros apply an operation f to the current list item x and then examine the next list item, peek, to determine whether to continue or not.
Finally, we assemble everything in the top-level MAP macro:
#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))
This macro adds a () marker at the end of the list and passes the whole thing through EVAL, returning the result.
The code is available as a library on GitHub for your convenience.
The above is the detailed content of How to Define a Macro That Operates on Arguments Passed to Another Macro in C?. For more information, please follow other related articles on the PHP Chinese website!