首頁  >  文章  >  後端開發  >  如何定義一個對 C 中傳遞給另一個巨集的參數進行操作的巨集?

如何定義一個對 C 中傳遞給另一個巨集的參數進行操作的巨集?

Linda Hamilton
Linda Hamilton原創
2024-11-15 15:02:03574瀏覽

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.

以上是如何定義一個對 C 中傳遞給另一個巨集的參數進行操作的巨集?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn