如何基于可变宏扩展多个宏
How to expand multiple macros based on variadic macro
我有很多最终生成代码的宏。例如:
#define CODE_GEN_IDENT1_HDR(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT2_HDR(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT1_SRC(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT2_SRC(PARAM1, PARAM2, PARAM3) \
// code generated
想法是 HDR
生成函数定义,SRC
生成它们的实现。所有宏都有相同数量的参数(在本例中为 3)。 IDENT
可以是MATH
、TRIG
、ALGS
、CONTAINERS
等任何名字,这就是我要重点说的。
我正在尝试构建一个宏,它可以使用可变参数宏生成所有这些具有不同 IDENT
的宏。例如:
// Concatenate macros to a standard form
#define CONCATH_(C) CODE_GEN_##C##_HDR
#define CONCATC_(C) CODE_GEN_##C##_SRC
// CONCATH concatenates to HDR
// CONCATC concatenates to SRC
#define CONCATH(C) CONCATH_(C)
#define CONCATC(C) CONCATC_(C)
#define MASTER_MACRO(PARAM1, PARAM2, PARAM3, ...) \
// Code that generates all other macros
// using CONCATH and CONCATC
// how could this be done?
当我写的时候:
MASTER_MACRO(int, "Hello", char *, MATH, TRIG, CONT)
我想要这样的东西:
CODE_GEN_MATH_HDR(int, "Hello", char *)
CODE_GEN_TRIG_HDR(int, "Hello", char *)
CODE_GEN_CONT_HDR(int, "Hello", char *)
CODE_GEN_MATH_SRC(int, "Hello", char *)
CODE_GEN_TRIG_SRC(int, "Hello", char *)
CODE_GEN_CONT_SRC(int, "Hello", char *)
以某种方式访问给定的参数并连接每个参数,使 header 和 source.
我目前拥有的是固定长度的宏,例如:
MASTER_MACRO(PARAM1, PARAM2, PARAM3, MATH, TRIG, CONT, DUPL, SORT) \
CONCATH(MATH)(PARAM1, PARAM2, PARAM3)
CONCATH(TRIG)(PARAM1, PARAM2, PARAM3)
...
CONCATC(MATH)(PARAM1, PARAM2, PARAM3)
...
当用户不想生成 CONT
、DUPL
或任何其他宏时,他必须传入 pre-defined 参数,例如 _
,这意味着他不想生成那个并且有一个空的宏:
#define CODE_GEN___SRC(PARAM1, PARAM2, PARAM3) // Empty
但这还不够好。在不同的项目中,这些不同的 IDENT
具有不同的名称,因此必须制作新的主宏有点烦人。
但最大的问题是:
- 这可以做到吗?
- 有没有简单的方法可以做到这一点?
- 可以仅使用标准 C 来完成吗(没有 compiler-dependent 宏)?
是的,你可以做到。对于手动实现,您可能希望从这样的基本参数计数器开始:
#define COUNT(...) \
COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,)
#define COUNT_I(_ ,_9,_8,_7,_6,_5,_4,_3,_2, X,...) X
参数计数器的工作方式有点像 "shift register",在计数之前注入参数列表。如果使用一个参数调用,则所有内容都将 X 与 1 对齐。每个额外的参数都会将此列表移动... 2 个参数将 2 移入 X,3 个参数将 3 移入,依此类推。这只是基本形式,最多支持 9 个参数,用于传达想法。
...现在您可以生成像这样的可变宏实用程序:
#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define TRANSFORM_CD(MACRO, ...) GLUE(TRANSFORM_CD_,COUNT(__VA_ARGS__))(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_1(MACRO,X) MACRO(X)
#define TRANSFORM_CD_2(MACRO,X,...) MACRO(X),TRANSFORM_CD_1(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_3(MACRO,X,...) MACRO(X),TRANSFORM_CD_2(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_4(MACRO,X,...) MACRO(X),TRANSFORM_CD_3(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_5(MACRO,X,...) MACRO(X),TRANSFORM_CD_4(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_6(MACRO,X,...) MACRO(X),TRANSFORM_CD_5(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_7(MACRO,X,...) MACRO(X),TRANSFORM_CD_6(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_8(MACRO,X,...) MACRO(X),TRANSFORM_CD_7(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_9(MACRO,X,...) MACRO(X),TRANSFORM_CD_8(MACRO,__VA_ARGS__)
概念上 TRANSFORM_CD
意味着 "transform" 一个逗号分隔列表(args 2 及以上)通过应用宏到另一个逗号分隔列表。在这种情况下,我们从一个以逗号分隔的基本名称列表(您在这里称之为 IDENT
)开始,并将您的一个转换宏应用于它;例如,TRANSFORM(CONCATH, TRIG, CONT, DUPL)
扩展为 CODE_GEN_TRIG_HDR, CODE_GEN_CONT_HDR, CODE_GEN_DUPL_HDR
。
接下来我们需要一些东西来生成具有多个宏和相同参数集的调用;喜欢这个实用程序:
#define INVOKE_ALL(TUPLE_, ...) GLUE(INVOKE_ALL_,COUNT(__VA_ARGS__))(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_1(TUPLE_, X) X TUPLE_
#define INVOKE_ALL_2(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_1(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_3(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_2(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_4(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_3(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_5(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_4(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_6(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_5(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_7(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_6(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_8(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_7(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_9(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_8(TUPLE_,__VA_ARGS__)
这里,TUPLE_
是一个带括号的参数列表(这使得该实用程序比要求它支持三个参数更通用);并且每个其他参数表示要使用这些参数调用的宏。
将两者结合起来,就是:
INVOKE_ALL((p1 a1, p2 a2, p3 a3),TRANSFORM_CD(CONCATH,MATH,TRIG,CONT,DUPL))
...应扩展为(为清楚起见重新格式化):
CODE_GEN_TRIG_HDR(p1 a1, p2 a2, p3 a3)
CODE_GEN_CONT_HDR(p1 a1, p2 a2, p3 a3)
CODE_GEN_DUPL_HDR(p1 a1, p2 a2, p3 a3)
...如果你真的想要,你可以保留 MASTER_MACRO
的形式和功能,只需使其可变,就像这样:
#define MASTER_MACRO(PARAM1, PARAM2, PARAM3, ...) \
INVOKE_ALL((PARAM1, PARAM2, PARAM3),TRANSFORM_CD(CONCATH,__VA_ARGS__)) \
INVOKE_ALL((PARAM1, PARAM2, PARAM3),TRANSFORM_CD(CONCATC,__VA_ARGS__))
我有很多最终生成代码的宏。例如:
#define CODE_GEN_IDENT1_HDR(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT2_HDR(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT1_SRC(PARAM1, PARAM2, PARAM3) \
// code generated
#define CODE_GEN_IDENT2_SRC(PARAM1, PARAM2, PARAM3) \
// code generated
想法是 HDR
生成函数定义,SRC
生成它们的实现。所有宏都有相同数量的参数(在本例中为 3)。 IDENT
可以是MATH
、TRIG
、ALGS
、CONTAINERS
等任何名字,这就是我要重点说的。
我正在尝试构建一个宏,它可以使用可变参数宏生成所有这些具有不同 IDENT
的宏。例如:
// Concatenate macros to a standard form
#define CONCATH_(C) CODE_GEN_##C##_HDR
#define CONCATC_(C) CODE_GEN_##C##_SRC
// CONCATH concatenates to HDR
// CONCATC concatenates to SRC
#define CONCATH(C) CONCATH_(C)
#define CONCATC(C) CONCATC_(C)
#define MASTER_MACRO(PARAM1, PARAM2, PARAM3, ...) \
// Code that generates all other macros
// using CONCATH and CONCATC
// how could this be done?
当我写的时候:
MASTER_MACRO(int, "Hello", char *, MATH, TRIG, CONT)
我想要这样的东西:
CODE_GEN_MATH_HDR(int, "Hello", char *)
CODE_GEN_TRIG_HDR(int, "Hello", char *)
CODE_GEN_CONT_HDR(int, "Hello", char *)
CODE_GEN_MATH_SRC(int, "Hello", char *)
CODE_GEN_TRIG_SRC(int, "Hello", char *)
CODE_GEN_CONT_SRC(int, "Hello", char *)
以某种方式访问给定的参数并连接每个参数,使 header 和 source.
我目前拥有的是固定长度的宏,例如:
MASTER_MACRO(PARAM1, PARAM2, PARAM3, MATH, TRIG, CONT, DUPL, SORT) \
CONCATH(MATH)(PARAM1, PARAM2, PARAM3)
CONCATH(TRIG)(PARAM1, PARAM2, PARAM3)
...
CONCATC(MATH)(PARAM1, PARAM2, PARAM3)
...
当用户不想生成 CONT
、DUPL
或任何其他宏时,他必须传入 pre-defined 参数,例如 _
,这意味着他不想生成那个并且有一个空的宏:
#define CODE_GEN___SRC(PARAM1, PARAM2, PARAM3) // Empty
但这还不够好。在不同的项目中,这些不同的 IDENT
具有不同的名称,因此必须制作新的主宏有点烦人。
但最大的问题是:
- 这可以做到吗?
- 有没有简单的方法可以做到这一点?
- 可以仅使用标准 C 来完成吗(没有 compiler-dependent 宏)?
是的,你可以做到。对于手动实现,您可能希望从这样的基本参数计数器开始:
#define COUNT(...) \
COUNT_I(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1,)
#define COUNT_I(_ ,_9,_8,_7,_6,_5,_4,_3,_2, X,...) X
参数计数器的工作方式有点像 "shift register",在计数之前注入参数列表。如果使用一个参数调用,则所有内容都将 X 与 1 对齐。每个额外的参数都会将此列表移动... 2 个参数将 2 移入 X,3 个参数将 3 移入,依此类推。这只是基本形式,最多支持 9 个参数,用于传达想法。
...现在您可以生成像这样的可变宏实用程序:
#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define TRANSFORM_CD(MACRO, ...) GLUE(TRANSFORM_CD_,COUNT(__VA_ARGS__))(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_1(MACRO,X) MACRO(X)
#define TRANSFORM_CD_2(MACRO,X,...) MACRO(X),TRANSFORM_CD_1(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_3(MACRO,X,...) MACRO(X),TRANSFORM_CD_2(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_4(MACRO,X,...) MACRO(X),TRANSFORM_CD_3(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_5(MACRO,X,...) MACRO(X),TRANSFORM_CD_4(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_6(MACRO,X,...) MACRO(X),TRANSFORM_CD_5(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_7(MACRO,X,...) MACRO(X),TRANSFORM_CD_6(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_8(MACRO,X,...) MACRO(X),TRANSFORM_CD_7(MACRO,__VA_ARGS__)
#define TRANSFORM_CD_9(MACRO,X,...) MACRO(X),TRANSFORM_CD_8(MACRO,__VA_ARGS__)
概念上 TRANSFORM_CD
意味着 "transform" 一个逗号分隔列表(args 2 及以上)通过应用宏到另一个逗号分隔列表。在这种情况下,我们从一个以逗号分隔的基本名称列表(您在这里称之为 IDENT
)开始,并将您的一个转换宏应用于它;例如,TRANSFORM(CONCATH, TRIG, CONT, DUPL)
扩展为 CODE_GEN_TRIG_HDR, CODE_GEN_CONT_HDR, CODE_GEN_DUPL_HDR
。
接下来我们需要一些东西来生成具有多个宏和相同参数集的调用;喜欢这个实用程序:
#define INVOKE_ALL(TUPLE_, ...) GLUE(INVOKE_ALL_,COUNT(__VA_ARGS__))(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_1(TUPLE_, X) X TUPLE_
#define INVOKE_ALL_2(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_1(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_3(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_2(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_4(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_3(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_5(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_4(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_6(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_5(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_7(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_6(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_8(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_7(TUPLE_,__VA_ARGS__)
#define INVOKE_ALL_9(TUPLE_, X,...) X TUPLE_ INVOKE_ALL_8(TUPLE_,__VA_ARGS__)
这里,TUPLE_
是一个带括号的参数列表(这使得该实用程序比要求它支持三个参数更通用);并且每个其他参数表示要使用这些参数调用的宏。
将两者结合起来,就是:
INVOKE_ALL((p1 a1, p2 a2, p3 a3),TRANSFORM_CD(CONCATH,MATH,TRIG,CONT,DUPL))
...应扩展为(为清楚起见重新格式化):
CODE_GEN_TRIG_HDR(p1 a1, p2 a2, p3 a3)
CODE_GEN_CONT_HDR(p1 a1, p2 a2, p3 a3)
CODE_GEN_DUPL_HDR(p1 a1, p2 a2, p3 a3)
...如果你真的想要,你可以保留 MASTER_MACRO
的形式和功能,只需使其可变,就像这样:
#define MASTER_MACRO(PARAM1, PARAM2, PARAM3, ...) \
INVOKE_ALL((PARAM1, PARAM2, PARAM3),TRANSFORM_CD(CONCATH,__VA_ARGS__)) \
INVOKE_ALL((PARAM1, PARAM2, PARAM3),TRANSFORM_CD(CONCATC,__VA_ARGS__))