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

时间:2019-05-04 23:46:24

标签: c macros c-preprocessor variadic-macros

我有很多最终生成代码的宏。例如:

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

要以某种方式访问​​给定的参数并连接每个参数,使它们既是标头又是源。

我目前拥有的是一个固定长度的宏,例如:

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或任何其他宏时,他必须传递一个像_这样的预定义参数,这意味着他不想生成并具有一个空宏:

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

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

但是最大的问题是:

  • 可以做到吗?
  • 有没有简单的方法可以做到这一点?
  • 可以仅使用标准C语言(不使用编译器相关宏)吗?

1 个答案:

答案 0 :(得分:1)

是的,您可以这样做。对于手动实施,您可能想从一个基本的参数计数器开始,如下所示:

#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

参数计数器的工作方式类似于“移位寄存器”,在计数之前注入参数列表。如果使用一个参数调用,则所有内容都将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旨在通过向其应用宏将“逗号分隔”列表(参数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

相关问题