首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >从宏的参数中移除最后尾随逗号

从宏的参数中移除最后尾随逗号
EN

Stack Overflow用户
提问于 2022-06-07 15:15:39
回答 2查看 428关注 0票数 10

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

所以我需要一个宏remove_trailing_comma(),它叫做remove_trailing_comma(arg1, arg2, arg3, ),扩展到arg1, arg2, arg3

我尝试过使用varargs和__VA_OPT__的不同组合,但我似乎无法做到这一点。

例如:

代码语言:javascript
运行
复制
#define discard_trailing_comma(arg, ...) \
    arg __VA_OPT__(,) discard_trailing_comma(__VA_ARGS__)

discard_trailing_comma(1, 2, 3, )

由于扩展到1 , discard_trailing_comma(2, 3,),我不知道为什么(宏不是递归展开的?)

这在C++20中是可能的吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-06-07 18:49:48

您可能应该使用@eljay的答案,但是如果您需要支持更多的参数,这里有一个在22行中支持~2000参数,并且增加更多的行以指数方式增长的参数。

代码语言:javascript
运行
复制
#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)

让我解释一下。

首先,当LOOPE4()中被调用时,它可以递归地调用自己,这是在LOOP0中完成的。这方面最简单的例子是#define LOOP(...) __VA_ARGS__ LOOP_ EMPTY() ()(__VA_ARGS__),它重复参数,直到递归限制,这是受E4嵌套约束的。Understanding DEFER and OBSTRUCT macros已经很好地解释了这种行为,所以我将跳过这部分解释。

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

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

LOOP1使用x将一个新的参数添加到我们的参数列表中。由于我们从一个空参数列表开始,我们将以一个前导空参数结束,我们可以在LOOP0上删除该参数。

PS:这个概念可以扩展到E5E6,.,尽管使用这个概念有更大的开销,因为一旦递归结束,预处理器仍然需要重新扫描结果直到递归限制。如果您想纠正这种情况,您可以使用order-pp中的延续机器,它实际上可以终止,但是它大约是150 loc。

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

代码语言:javascript
运行
复制
#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)
票数 9
EN

Stack Overflow用户

发布于 2022-06-07 15:59:07

假设您有一定数量的参数,您可以让一个类似于委托函数的宏作为一个助手来选择适当的实现宏。

代码语言:javascript
运行
复制
#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, )
};
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72533711

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档