也许有更好的方法
为此拍摄。
不允许将结构化绑定包装在 `()` 中
...
如果有的话,是否有任何好的技术可以通过匹配开头的 `[` 和关闭的 `]` 来对参数进行分组?
这里有两个事实:(1)预处理器将匹配()
's,并且只匹配()
's(没有其他分组运算符)。(2) 结构化绑定不能用括号括起来。(1) 表示您不想在宏中使用[
/ ]
;你想用()
's。(2) 并不真正冲突;这意味着你想用[]
's 而不是()
's 离开翻译阶段 4。仅供参考,我将使用术语tuple来指代括号匹配、逗号分隔的标记列表(与 boost 预处理器的术语一致)。
因此,让我们考虑一下元组参数是结构绑定,标识符是普通变量绑定的想法。从表单的角度来看,我们只需要一个带有(a,b)
to[a,b]
和c
to的宏c
。这是模式匹配的工作。
第 1 部分:对元组求平方
在这里,我将使用一个间接THIRD
宏,它只是间接地扩展到它的第三个参数。间接部分是偷偷摸摸的部分;这让我们可以构造一个通常会被忽略的一次性第一个参数。但是如果第一个参数调用一个宏,则扩展可以有一个逗号,这会将第二个参数移到第三个位置,然后再选择第三个参数。检测参数A
是否是括号很容易;只需在它前面加上一个类似函数的宏名称;如果它是括号,那就是调用......如果不是,它只是两个标记。以下是如何利用它来制作我们的宏:
#define M_THIRD(...) M_THIRD_(__VA_ARGS__,,,)
#define M_THIRD_(A,B,C,...) C
#define M_SQUARETUPLE(X) M_THIRD(M_PARDETECT X, M_SQUARE, ) X
#define M_PARDETECT(...) ,
#define M_SQUARE(...) [__VA_ARGS__]
第 2 部分:计数、可分隔的迭代器
对我来说,遍历一个元组比遍历“除了最后一个参数之外的所有内容”更自然。因此,让我们假设我们正在针对您的示例案例,并且在处理的某个步骤中我们有M_LAMBDIZE_PAIR( ((x,y),c,(f,g,h)), f*x - y + c / h * g)
. 我们希望能够迭代第一个元组,但是在迭代它时,我们希望有一个与每个元素的序数位置相对应的数字(这样我们就可以构建类似的东西__arg1
),并且我们希望能够使用一个分隔符里面有逗号(所以我们可以生成auto&& __arg1, auto&& __arg2, auto&& __arg3
)。因此,让我们构建一个迭代器并专门针对这些功能进行规划。元组是一般包装可能有逗号的分隔符的好方法,因为预处理器再次匹配括号。
所以这是一个符合要求的通用迭代构造:
#define M_INC(X) M_CONC(M_INC_,X)
#define M_INC_1 2
#define M_INC_2 3
#define M_INC_3 4
#define M_INC_4 5
#define M_INC_5 6
#define M_INC_6 7
#define M_INC_7 8
#define M_INC_8 9
#define M_INC_9 10
#define M_UNWRAP(...) __VA_ARGS__
#define M_TOEACH(M,L,...) M_CONC(M_TOEACH_,M_NARGS(__VA_ARGS__))(M,L,1,__VA_ARGS__)
#define M_TOEACH_1(M,L,I,A) M(I,A)
#define M_TOEACH_2(M,L,I,A,...) M(I,A) M_UNWRAP L M_TOEACH_1(M,L,M_INC(I),__VA_ARGS__)
#define M_TOEACH_3(M,L,I,A,...) M(I,A) M_UNWRAP L M_TOEACH_2(M,L,M_INC(I),__VA_ARGS__)
#define M_TOEACH_4(M,L,I,A,...) M(I,A) M_UNWRAP L M_TOEACH_3(M,L,M_INC(I),__VA_ARGS__)
#define M_TOEACH_5(M,L,I,A,...) M(I,A) M_UNWRAP L M_TOEACH_4(M,L,M_INC(I),__VA_ARGS__)
#define M_TOEACH_6(M,L,I,A,...) M(I,A) M_UNWRAP L M_TOEACH_5(M,L,M_INC(I),__VA_ARGS__)
#define M_TOEACH_7(M,L,I,A,...) M(I,A) M_UNWRAP L M_TOEACH_6(M,L,M_INC(I),__VA_ARGS__)
#define M_TOEACH_8(M,L,I,A,...) M(I,A) M_UNWRAP L M_TOEACH_7(M,L,M_INC(I),__VA_ARGS__)
#define M_TOEACH_9(M,L,I,A,...) M(I,A) M_UNWRAP L M_TOEACH_8(M,L,M_INC(I),__VA_ARGS__)
#define M_TOEACH_10(M,L,I,A,...) M(I,A) M_UNWRAP L M_TOEACH_9(M,L,M_INC(I),__VA_ARGS__)
M_TOEACH
接受一个宏M
、一个括号括起来的分隔符L
和一个要迭代的参数列表。对于每个参数,它调用M(I,A)
whereI
是参数的序数并且A
是参数本身。 M_UNWRAP
始终应用于分隔符;所以我们可以传入(,)
逗号分隔,或者只是()
省略具有相同构造的分隔符。
辅助间接调用宏:
#define M_CALL(...) M_CALL_(__VA_ARGS__)
#define M_CALL_(M,...) M(__VA_ARGS__)
...以宏名称之类的函数作为第一个参数,其参数如下,并执行间接调用。如果我们想要展开一个元组以迭代它,这很有用,但是在我们进行实际调用时完全应用该展开。使用它,这里有一个例子M_LAMBDIZE_PAIR
:
#define M_PRDECL(I,A) auto&& M_CONC(__arg,I)
#define M_ARDECL(I,A) auto& M_SQUARETUPLE(A) = M_CONC(__arg,I);
#define M_LAMBDIZE_PAIR(ARGS, EXPR) \
[&]( M_CALL(M_TOEACH,M_PRDECL,(,),M_UNWRAP ARGS)) { \
M_CALL(M_TOEACH,M_ARDECL,(),M_UNWRAP ARGS) \
return EXPR; \
}
更完整的例子:https ://godbolt.org/z/xTh1WI