Home  >  Article  >  Backend Development  >  How to Define a Macro That Operates on Arguments Passed to Another Macro in C?

How to Define a Macro That Operates on Arguments Passed to Another Macro in C?

Linda Hamilton
Linda HamiltonOriginal
2024-11-15 15:02:03576browse

How to Define a Macro That Operates on Arguments Passed to Another Macro in C?

Macros Within Macro Arguments

The Problem

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);

Achieving Recursion

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 */

Basic Recursion

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.

End Detection

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.

Putting it All Together

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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn