从宏参数中删除最后一个尾随逗号

gig*_*tes 12 c++ c-preprocessor c++20

我需要从宏参数列表中删除最后一个尾随逗号(因为它们最终将扩展为不承认尾随逗号的模板参数)。

所以我需要一个remove_trailing_comma()名为 like 的宏remove_trailing_comma(arg1, arg2, arg3, )扩展为arg1, arg2, arg3.

我尝试过不同的可变参数组合,__VA_OPT__但似乎无法做到。

例如:

#define discard_trailing_comma(arg, ...) \
    arg __VA_OPT__(,) discard_trailing_comma(__VA_ARGS__)

discard_trailing_comma(1, 2, 3, )
Run Code Online (Sandbox Code Playgroud)

不起作用(使用 g++ 10),因为扩展为1 , discard_trailing_comma(2, 3,),我不知道为什么(宏不是递归扩展的吗?)

这在 C++20 中可能吗?

cam*_*cdr 10

您可能应该采用 @eljay 的答案,但如果您需要支持更多参数,这里有一个在 22 行中支持约 2000 个参数的答案,并且添加更多行会使该数字呈指数级增长。

#define E4(...) E3(E3(E3(E3(E3(E3(E3(E3(E3(E3(__VA_ARGS__))))))))))
#define E3(...) E2(E2(E2(E2(E2(E2(E2(E2(E2(E2(__VA_ARGS__))))))))))
#define E2(...) E1(E1(E1(E1(E1(E1(E1(E1(E1(E1(__VA_ARGS__))))))))))
#define E1(...) __VA_ARGS__

#define EMPTY()
#define TUPLE_AT_2(x,y,...) y
#define TUPLE_TAIL(x,...) __VA_ARGS__

#define CHECK(...) TUPLE_AT_2(__VA_ARGS__,0,)
#define EQ_END_END ,1

#define SCAN(...) __VA_ARGS__
#define CAT(a,b) CAT_(a,b)
#define CAT_(a,b) a##b

#define LOOP_() LOOP
#define LOOP(x,y,...) CAT(LOOP, CHECK(EQ_END_##y))(x,y,__VA_ARGS__)
#define LOOP1(x,...) (TUPLE_TAIL x)
#define LOOP0(x,y,...) LOOP_ EMPTY() ()((SCAN x, y),__VA_ARGS__)

#define DTC(...) E4(LOOP((),    __VA_ARGS__ END))

DTC (1, 2, 3, 4, 5, 6, 7, 8, 9,) // expands to: (1, 2, 3, 4, 5, 6, 7, 8, 9)
Run Code Online (Sandbox Code Playgroud)

让我尝试解释一下这一点。

首先,当LOOP内部调用时E4(),它可以递归地调用自身,这是在LOOP0中完成的。最简单的例子是#define LOOP(...) __VA_ARGS__ LOOP_ EMPTY() ()(__VA_ARGS__)重复参数直到递归限制,这是由 的嵌套限制的E4理解 DEFER 和 OBSTRUCT 宏已经很好地解释了该行为,因此我将跳过这部分解释。

现在的想法如下:我们循环遍历每个参数,直到到达最后一个参数,我们在其中插入了一个END标记。在这样做的同时,我们构建了一个新的参数列表,但是当我们到达END

CAT(LOOP, CHECK(EQ_END_##y))LOOP1如果参数y包含结束标记,则分支到END,否则分支到LOOP0

LOOP1x在using中将一个新参数附加到我们的参数列表中(SCAN x, y)。由于我们从一个空参数列表开始,因此我们最终会得到一个前导空参数,我们可以在 上轻松删除它LOOP0

PS:这个概念可以简单地扩展到E5, E6, ... ,尽管使用它会产生更大的开销,因为一旦递归结束,预处理器仍然需要重新扫描结果直到递归限制。如果你想解决这个问题,你可以使用 order-pp 中的延续机之类的东西,它实际上可以终止,但它大约是 150 loc。

编辑,我刚刚重新审视了这一点,并意识到使用x构建元组的效率非常低,这是一个不这样做的版本:

#define E4(...) E3(E3(E3(E3(E3(E3(E3(E3(E3(E3(__VA_ARGS__))))))))))
#define E3(...) E2(E2(E2(E2(E2(E2(E2(E2(E2(E2(__VA_ARGS__))))))))))
#define E2(...) E1(E1(E1(E1(E1(E1(E1(E1(E1(E1(__VA_ARGS__))))))))))
#define E1(...) __VA_ARGS__

#define EMPTY()
#define FX(f,x) f(x)
#define TUPLE_AT_2(x,y,...) y
#define TUPLE_TAIL(x,...) __VA_ARGS__

#define CHECK(...) TUPLE_AT_2(__VA_ARGS__,0,)
#define EQ_END_END ,1

#define CAT(a,b) CAT_(a,b)
#define CAT_(a,b) a##b

#define LOOP_() LOOP
#define LOOP(x,...) CAT(LOOP, CHECK(EQ_END_##x))(x,__VA_ARGS__)
#define LOOP1(x,...) 
#define LOOP0(x,...) LOOP_ EMPTY() ()(__VA_ARGS__),x

#define DTC(...) (FX(TUPLE_TAIL,E4(LOOP(__VA_ARGS__ END))))

DTC (1, 2, 3, 4, 5, 6, 7, 8, 9,) // expands to: (1, 2, 3, 4, 5, 6, 7, 8, 9)
Run Code Online (Sandbox Code Playgroud)


Elj*_*jay 8

假设您的参数数量有限,您可以创建一个类似委托函数的宏作为帮助器来选择适当的实现宏。

#define GET_DTC(_1,_2,_3,_4,NAME,...) NAME
#define DTC4(_1,_2,_3,_4) _1,_2,_3
#define DTC3(_1,_2,_3) _1,_2
#define DTC2(_1,_2) _1
#define DTC1(_1)
#define discard_trailing_comma(...) \
    GET_DTC(__VA_ARGS__, DTC4, DTC3, DTC2, DTC1)(__VA_ARGS__)

int arr[] = {
discard_trailing_comma(1, 2, 3, )
};
Run Code Online (Sandbox Code Playgroud)