Swallowing comma in variadic macros on compilers that do not recognise ##

后端 未结 2 1802
礼貌的吻别
礼貌的吻别 2021-01-14 06:28

I need to write a variadic macro in C which must take zero or more arguments.

In gcc, that can be achieved by adding \"##\" after the comma, e.g. ,##____VA_ARG

2条回答
  •  孤街浪徒
    2021-01-14 06:51

    Using a simplified version what Jens spelled out, a possible approach is described below.

    E_ is used to inject an argument, while I_ is used to cause indirect invocation of a macro. V_ is required to add an additional level of indirection, which seems required for some compilers.

    #define V_(...) __VA_ARGS__
    #define I_(M,...) V_(M(__VA_ARGS__))
    #define E_() _,
    

    Now, suppose you need a macro MAC to handle 0, 1, or 2 arguments:

    MAC(1, 2);
    MAC(1);
    MAC();
    

    One possible implementation might look like:

    #define MAC_X(_2,_1,X,...) MAC_##X
    #define MAC(...) V_(V_(MAC_X(__VA_ARGS__,2,_))(__VA_ARGS__))
    
    #define MAC_2(...) function_for_2(__VA_ARGS__)
    #define MAC_1(...) function_for_1(__VA_ARGS__)
    #define MAC_0(...) function_for_0(__VA_ARGS__)
    
    #define MAC_Y(_1,Y,...) MAC_##Y
    #define MAC__(...) I_(MAC_Y, E_ __VA_ARGS__ () 0, 1)(__VA_ARGS__)
    

    And expanding it to 3 arguments is relatively straightforward. The only messiness is detecting 1 or 0 arguments, which does not change:

    -#define MAC_X(_2,_1,X,...) MAC_##X
    -#define MAC(...) V_(V_(MAC_X(__VA_ARGS__,2,_))(__VA_ARGS__))
    +#define MAC_X(_3,_2,_1,X,...) MAC_##X
    +#define MAC(...) V_(V_(MAC_X(__VA_ARGS__,3,2,_))(__VA_ARGS__))
    
    +#define MAC_3(...) function_for_3(__VA_ARGS__)
     #define MAC_2(...) function_for_2(__VA_ARGS__)
    

    For more than 1 argument, the MAC macro expands into MAC_2 (or MAC_3). But, for 1 or 0 arguments, the MAC macro expands to MAC__.

    The MAC__ macro applies Jens' trick to detect whether it was passed 1 or 0 arguments. It does this with the E_ helper macro, and injecting its argument between E_ and the () that would cause it to be invoked. If there are 0 arguments, the E_ is invoked, and an argument is injected. The injected argument causes 0 to be the second argument for MAC_Y to select. If there is 1 argument, E_ is not expanded. The first argument to the to MAC_Y becomes E_ ... () 0 (where the ... is whatever the first argument was), and the second argument for MAC_Y is 1. This allows MAC_Y to invoke either MAC_0 or MAC_1.

提交回复
热议问题