对于 C 预处理器来说,这很困难的部分原因是它需要递归到括号中的分组中。这实际上意味着我们需要在外部级别应用延迟/评估,或者我们需要在内部级别应用不同的宏。递归级别的规范还存在不对称性,虽然这本身并不难,但必须加以考虑。
如果需要,我可以假设最大嵌套深度 w.r.t。括号,比如说四个级别。
我将致力于此...并支持四个级别。
定义
定义(使用与 boost 预处理器兼容的术语):元组是一种预处理数据结构,由单个括号分组组成,其元素以逗号分隔。所以(a,(b,c),d) 是一个包含三个元素的元组...a、(b,c) 和d。 sequence 是一种预处理数据结构,由一系列连续元素组成,每个元素都由括号括起来。所以上面列出的相同元素用序列(a)((b,c))(d)表示。
设计
要使用可变参数处理元组,我们需要一个计数器、一个粘合宏以及一个用于我们想要支持的每个元组大小的宏。如果说我们支持从 1 到 10 个元素的元组,并且想要将其更改为支持从 1 到 20 的元组,我们可能必须更改计数器并添加 10 个更多的宏。但是如果我们仅使用元组支持 4 级递归,我们需要 4 倍于这个数量的宏;这使得扩展更加繁重。
您展示了两种形式的宏;两者都使用元组,后者只使用顶层的序列。为了保持一致性,我将选择第一种形式。但是为了可扩展性,我将添加元组处理只是为了将元组转换为序列,显示 9 元素元组支持......然后,要支持 n 元素元组,您只需要更改计数并添加 n-9 个宏。
方法
我们从一个基本的粘合宏、一个计数和一个单一级别的序列转换器开始 (PARN):
#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define PARN(...) GLUE(PARN_, COUNT (__VA_ARGS__)) (__VA_ARGS__)
#define PARN_9(A,B,C,D,E,F,G,H,I) (A)(B)(C)(D)(E)(F)(G)(H)(I)
...
#define PARN_2(A,B) (A)(B)
#define PARN_1(A) (A)
我们想要迭代另一个级别或只处理一个元素,具体取决于它是否是括号。为此,我将应用基于“间接第二宏”的模式匹配器。这个想法是你在参数 1 中放置一个模式并设置宏,这样,如果一个模式匹配,它将标记转移到参数 2;否则,您的模式只会在参数 1 中生成一堆被忽略的标记。这是带有括号检测器的模式匹配器构造(请注意,如果您传递 1 个参数,我的第二个扩展为空):
#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(A,B,...) B
#define CALLDETECT(...) ,
鉴于您提供的链接,您应该已经知道如何应用序列。我有一个用于序列应用的特定宏:
#define PASTE_E(...) PASTE_E_I(__VA_ARGS__)
#define PASTE_E_I(...) __VA_ARGS__ ## E
...典型的“简单”序列处理在两个宏之间切换,称它们为A 和B,直到它们到达终端...所以它们转到 A,B,A,B,..., E.但是我们想留下一个逗号分隔的结果,没有额外的逗号。因此,我们将在每个宏中添加逗号,但我们将从另一个不添加的 A 开始;即,我们要去 A,B,C,B,C,...,E。我们需要 这些 组中的四个来 (a) 避免蓝色油漆,以及 (b) 在顶层进行不同的处理。这是初始设置:
#define BRACP0(X) {SECOND(CALLDETECT X MAGIC1)X}
#define BRAC0_A(X) BRACP0(X)BRAC0_B
#define BRAC0_B(X) ,BRACP0(X)BRAC0_C
#define BRAC0_C(X) ,BRACP0(X)BRAC0_B
#define BRAC0_BE
#define BRAC0_CE
这会将外部级别MAGIC链接到内部级别MAGIC1;这个集合专门为每个规范添加了一个额外的 {} 对 (BRACP0)。除了{} 之外,其余的集合看起来都差不多。但是对于最后一个级别,我们希望调用一个简单的 bracify 而不是“下一个魔术”(这也是可扩展的;您需要每个级别的递归深度支持这些集合之一)。 bracify 和 4 级的“BRACP”看起来像这样:
#define BRACIFY(...) {__VA_ARGS__}
#define BRACP4(X) SECOND(CALLDETECT X BRACIFY)X
每个魔术宏级别都有一个“展开”:
#define MAGIC(...) {PASTE_E(MAGIC_U(BRAC0_A PARN(__VA_ARGS__)))}
#define MAGIC_U(...) __VA_ARGS__
#define MAGIC1(...) {PASTE_E(MAGIC1_U(BRAC1_A PARN(__VA_ARGS__)))}
#define MAGIC1_U(...) __VA_ARGS__
...
#define MAGIC4(...) {PASTE_E(MAGIC4_U(BRAC3_A PARN(__VA_ARGS__)))}
#define MAGIC4_U(...) __VA_ARGS__
完整的解决方案和演示
链接到coliru.stacked-crooked 演示。
最后的比赛
我将 C99 解释为此处的标准含义,而这个问题只是“是否可能”;因此,我没有费心将其转换为与 MSVS 的预处理器一起使用。它可能不会按原样工作。让我知道这是否让您感到困扰。