问题描述
我想将一个字符串化的宏参数附加到可变参数宏中的每个元素.我想我知道我需要什么,但我还没有想出一个可行的解决方案.给定一个可变参数宏,例如:
I'd like to append a stringified macro argument to each element in a variadic macro. I think I know what I need, but I couldn't come up with a working solution just yet. Given a variadic macro like:
#define FIELD_DECLARATION(NAME, OTHER_FIELD, ...)
FIELD_DECLARATION(First, Thing)
FIELD_DECLARATION(Second, Thing, Thing, Nothing)
我想生成:
field_First = {ThingArg};
field_Second = {ThingArg, ThingArg, NothingArg};
我想我需要的是递归地继续扩展 __VA_ARGS__
直到它到达没有元素,并在扩展时附加 "Arg"
.最后,将结果传递给另一个可变参数宏,该宏生成以逗号分隔的参数列表.
I guess what I need is to recursively keep expanding __VA_ARGS__
until it reaches no elements, and append "Arg"
while doing expansion. Finally, pass the result to another variadic macro that produces a comma-separated-list of arguments.
我已经尝试过了,但它不起作用(我描述的也不是这样):
I've tried this, which wouldn't work (and it isn't what I described, either):
#define UNPACK_VA_1(A1) A1 ## Arg
#define UNPACK_VA_2(A1, A2) UNPACK_VA_1(A1), UNPACK_VA_1(A2)
#define UNPACK_VA_3(A1, A2, A3) UNPACK_VA_2(A1, A2), UNPACK_VA_1(A3)
#define UNPACK_VA_4(A1, A2, A3, A4) UNPACK_VA_2(A1, A2), UNPACK_VA_2(A3, A4)
#define UNPACK_VA(...) UNPACK_VA_4(__VA_ARGS__)
#define FOO(x, y, ...) UNPACK_VA(__VA_ARGS__)
FOO(One, Two, Three, Four, Five, Six)
虽然这在一定程度上可行,但我无法提出可扩展的解决方案.如果有人能提供一盏灯,那就太好了.
While this somewhat works, I couldn't come up with a scalable solution. It'd be great if someone could shed a light.
推荐答案
这是一种可扩展的方法.首先,一些通用的实用宏:
Here's one scalable approach. First, some general utility macros:
#define EVAL(...) __VA_ARGS__
#define VARCOUNT(...)
EVAL(VARCOUNT_I(__VA_ARGS__,9,8,7,6,5,4,3,2,1,))
#define VARCOUNT_I(_,_9,_8,_7,_6,_5,_4,_3,_2,X_,...) X_
#define GLUE(X,Y) GLUE_I(X,Y)
#define GLUE_I(X,Y) X##Y
#define FIRST(...) EVAL(FIRST_I(__VA_ARGS__,))
#define FIRST_I(X,...) X
#define TUPLE_TAIL(...) EVAL(TUPLE_TAIL_I(__VA_ARGS__))
#define TUPLE_TAIL_I(X,...) (__VA_ARGS__)
#define TRANSFORM(NAME_, ARGS_) (GLUE(TRANSFORM_,VARCOUNT ARGS_)(NAME_, ARGS_))
#define TRANSFORM_1(NAME_, ARGS_) NAME_ ARGS_
#define TRANSFORM_2(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_1(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_3(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_2(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_4(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_3(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_5(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_4(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_6(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_5(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_7(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_6(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_8(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_7(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_9(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_8(NAME_,TUPLE_TAIL ARGS_)
在语义上,VARCOUNT
对参数进行计数;GLUE
是典型的间接粘贴;FIRST
提取第一个参数;EVAL
扩展到它的参数(意图评估),TUPLE_TAIL
返回元组的尾部(即,它丢弃第一个参数).
Semantically, VARCOUNT
counts arguments; GLUE
is a typical indirect paster; FIRST
extracts the first argument; EVAL
expands to its arguments (with intent to evaluate), and TUPLE_TAIL
returns the tail of a tuple (i.e., it discards the first argument).
TRANSFORM
这里是主要思想;TRANSFORM(FOO,(X,Y,Z))
采用元组 (X,Y,Z)
到 (FOO(X),FOO(Y),FOO(Z))
.
TRANSFORM
here is the main idea; TRANSFORM(FOO,(X,Y,Z))
takes a tuple (X,Y,Z)
to (FOO(X),FOO(Y),FOO(Z))
.
就位,这是专用代码:
#define Z_ARG(X) GLUE(X,Arg)
#define MAKE_INITIALIZER(...) { __VA_ARGS__ }
#define FIELD_DECLARATION(FNAME_, ...)
GLUE(field_, FNAME_) = EVAL(MAKE_INITIALIZER TRANSFORM(Z_ARG, (__VA_ARGS__)));
鉴于上述情况,这应该是可读的,但只是为了解释...... Z_ARG
将 Arg
粘贴到一个项目;MAKE_INITIALIZER
将预处理器元组转换为初始化列表;FIELD_DECLARATION
是你的宏.请注意,EVAL
包装了 MAKE_INITIALIZER
/transformed 元组,因此它实际上会调用该宏.
Given the above, this should be readable, but just to explain... Z_ARG
pastes Arg
to an item; MAKE_INITIALIZER
transforms a preprocessor tuple to an initialization list; and FIELD_DECLARATION
is your macro. Note that EVAL
wraps the MAKE_INITIALIZER
/transformed tuple so it will actually call that macro.
注意:将 EVAL
移到顶部并在更多地方使用它,这样在 MSVC 中也可以使用.
Note: Moved EVAL
to the top and used it in a few more places, such that this will work in MSVC as well.
这篇关于预处理器:将字符串连接到 __VA_ARGS__ 中的每个参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!