如何基于可变宏扩展多个宏

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可以是MATHTRIGALGSCONTAINERS等任何名字,这就是我要重点说的。

我正在尝试构建一个宏,它可以使用可变参数宏生成所有这些具有不同 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)
    ...

当用户不想生成 CONTDUPL 或任何其他宏时,他必须传入 pre-defined 参数,例如 _,这意味着他不想生成那个并且有一个空的宏:

#define CODE_GEN___SRC(PARAM1, PARAM2, PARAM3) // Empty

但这还不够好。在不同的项目中,这些不同的 IDENT 具有不同的名称,因此必须制作新的主宏有点烦人。

但最大的问题是:

是的,你可以做到。对于手动实现,您可能希望从这样的基本参数计数器开始:

#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__))

demo at stacked-crooked