Overloading Macro on Number of Arguments

后端 未结 8 1559
离开以前
离开以前 2020-11-22 04:25

I have two macros FOO2 and FOO3:

#define FOO2(x,y) ...
#define FOO3(x,y,z) ...

I want to define a new macro

相关标签:
8条回答
  • 2020-11-22 04:48

    To add on to netcoder's answer, you CAN in fact do this with a 0-argument macro, with the help of the GCC ##__VA_ARGS__ extension:

    #define GET_MACRO(_0, _1, _2, NAME, ...) NAME
    #define FOO(...) GET_MACRO(_0, ##__VA_ARGS__, FOO2, FOO1, FOO0)(__VA_ARGS__)
    
    0 讨论(0)
  • 2020-11-22 04:51

    Here is a more general solution:

    // get number of arguments with __NARG__
    #define __NARG__(...)  __NARG_I_(__VA_ARGS__,__RSEQ_N())
    #define __NARG_I_(...) __ARG_N(__VA_ARGS__)
    #define __ARG_N( \
          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,N,...) N
    #define __RSEQ_N() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0
    
    // general definition for any function name
    #define _VFUNC_(name, n) name##n
    #define _VFUNC(name, n) _VFUNC_(name, n)
    #define VFUNC(func, ...) _VFUNC(func, __NARG__(__VA_ARGS__)) (__VA_ARGS__)
    
    // definition for FOO
    #define FOO(...) VFUNC(FOO, __VA_ARGS__)
    

    Define your functions:

    #define FOO2(x, y) ((x) + (y))
    #define FOO3(x, y, z) ((x) + (y) + (z))
    
    // it also works with C functions:
    int FOO4(int a, int b, int c, int d) { return a + b + c + d; }
    

    Now you can use FOO with 2, 3 and 4 arguments:

    FOO(42, 42) // will use makro function FOO2
    FOO(42, 42, 42) // will use makro function FOO3
    FOO(42, 42, 42, 42) // will call FOO4 function
    

    Limitations

    • Only up to 63 arguments (but expandable)
    • Function for no argument only in GCC possible

    Ideas

    Use it for default arguments:

    #define func(...) VFUNC(func, __VA_ARGS__)
    #define func2(a, b) func4(a, b, NULL, NULL)
    #define func3(a, b, c) func4(a, b, c, NULL)
    
    // real function:
    int func4(int a, int b, void* c, void* d) { /* ... */ }
    

    Use it for functions with possible infinite number of arguments:

    #define SUM(...) VFUNC(SUM, __VA_ARGS__)
    #define SUM2(a, b) ((a) + (b))
    #define SUM3(a, b, c) ((a) + (b) + (c))
    #define SUM4(a, b, c) ((a) + (b) + (c) + (d))
    // ...
    

    PS: __NARG__ is copied from Laurent Deniau & Roland Illig here: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1

    0 讨论(0)
  • 2020-11-22 04:54

    Simple as:

    #define GET_MACRO(_1,_2,_3,NAME,...) NAME
    #define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2)(__VA_ARGS__)
    

    So if you have these macros:

    FOO(World, !)         # expands to FOO2(World, !)
    FOO(foo,bar,baz)      # expands to FOO3(foo,bar,baz)
    

    If you want a fourth one:

    #define GET_MACRO(_1,_2,_3,_4,NAME,...) NAME
    #define FOO(...) GET_MACRO(__VA_ARGS__, FOO4, FOO3, FOO2)(__VA_ARGS__)
    
    FOO(a,b,c,d)          # expeands to FOO4(a,b,c,d)
    

    Naturally, if you define FOO2, FOO3 and FOO4, the output will be replaced by those of the defined macros.

    0 讨论(0)
  • 2020-11-22 04:55

    This seems to work fine on GCC, Clang and MSVC. It's a cleaned up version of some of the answers here

    #define _my_BUGFX(x) x
    
    #define _my_NARG2(...) _my_BUGFX(_my_NARG1(__VA_ARGS__,_my_RSEQN()))
    #define _my_NARG1(...) _my_BUGFX(_my_ARGSN(__VA_ARGS__))
    #define _my_ARGSN(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) N
    #define _my_RSEQN() 10,9,8,7,6,5,4,3,2,1,0
    
    #define _my_FUNC2(name,n) name ## n
    #define _my_FUNC1(name,n) _my_FUNC2(name,n)
    #define GET_MACRO(func,...) _my_FUNC1(func,_my_BUGFX(_my_NARG2(__VA_ARGS__))) (__VA_ARGS__)
    
    #define FOO(...) GET_MACRO(FOO,__VA_ARGS__)
    
    0 讨论(0)
  • 2020-11-22 05:00

    I was just researching this myself, and I came across this here. The author added default argument support for C functions via macros.

    I'll try to briefly summarize the article. Basically, you need to define a macro that can count arguments. This macro will return 2, 1, 0, or whatever range of arguments it can support. Eg:

    #define _ARG2(_0, _1, _2, ...) _2
    #define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)
    

    With this, you need to create another macro that takes a variable number of arguments, counts the arguments, and calls the appropriate macro. I've taken your example macro and combined it with the article's example. I have FOO1 call function a() and FOO2 call function a with argument b (obviously, I'm assuming C++ here, but you can change the macro to whatever).

    #define FOO1(a) a();
    #define FOO2(a,b) a(b);
    
    #define _ARG2(_0, _1, _2, ...) _2
    #define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)
    
    #define _ONE_OR_TWO_ARGS_1(a) FOO1(a)
    #define _ONE_OR_TWO_ARGS_2(a, b) FOO2(a,b)
    
    #define __ONE_OR_TWO_ARGS(N, ...) _ONE_OR_TWO_ARGS_ ## N (__VA_ARGS__)
    #define _ONE_OR_TWO_ARGS(N, ...) __ONE_OR_TWO_ARGS(N, __VA_ARGS__)
    
    #define FOO(...) _ONE_OR_TWO_ARGS(NARG2(__VA_ARGS__), __VA_ARGS__)
    

    So if you have

    FOO(a)
    FOO(a,b)
    

    The preprocessor expands that to

    a();
    a(b);
    

    I would definitely read the article that I linked. It's very informative and he mentions that NARG2 won't work on empty arguments. He follows this up here.

    0 讨论(0)
  • 2020-11-22 05:08

    Here is a more compact version of the answer above. With example.

    #include <iostream>
    using namespace std;
    
    #define OVERLOADED_MACRO(M, ...) _OVR(M, _COUNT_ARGS(__VA_ARGS__)) (__VA_ARGS__)
    #define _OVR(macroName, number_of_args)   _OVR_EXPAND(macroName, number_of_args)
    #define _OVR_EXPAND(macroName, number_of_args)    macroName##number_of_args
    
    #define _COUNT_ARGS(...)  _ARG_PATTERN_MATCH(__VA_ARGS__, 9,8,7,6,5,4,3,2,1)
    #define _ARG_PATTERN_MATCH(_1,_2,_3,_4,_5,_6,_7,_8,_9, N, ...)   N
    
    
    //Example:
    #define ff(...)     OVERLOADED_MACRO(ff, __VA_ARGS__)
    #define ii(...)     OVERLOADED_MACRO(ii, __VA_ARGS__)
    
    #define ff3(c, a, b) for (int c = int(a); c < int(b); ++c)
    #define ff2(c, b)   ff3(c, 0, b)
    
    #define ii2(a, b)   ff3(i, a, b)
    #define ii1(n)      ii2(0, n)
    
    
    int main() {
        ff (counter, 3, 5)
            cout << "counter = " << counter << endl;
        ff (abc, 4)
            cout << "abc = " << abc << endl;
        ii (3)
            cout << "i = " << i << endl;
        ii (100, 103)
            cout << "i = " << i << endl;
    
    
        return 0;
    }
    

    Run:

    User@Table 13:06:16 /c/T
    $ g++ test_overloaded_macros.cpp 
    
    User@Table 13:16:26 /c/T
    $ ./a.exe
    counter = 3
    counter = 4
    abc = 0
    abc = 1
    abc = 2
    abc = 3
    i = 0
    i = 1
    i = 2
    i = 100
    i = 101
    i = 102
    

    Note that having both _OVR and _OVR_EXPAND may look redundant, but it's necessary for the preprocessor to expand the _COUNT_ARGS(__VA_ARGS__) part, which otherwise is treated as a string.

    0 讨论(0)
提交回复
热议问题