C Macros: How to map another macro to variadic arguments?

前端 未结 3 2040
离开以前
离开以前 2020-12-19 22:45

I\'d like to know how to apply a unary function (or another macro) to variadic arguments of a macro, like

int f(int a);

#define apply(args...) 

        
相关标签:
3条回答
  • 2020-12-19 23:25

    The code below is working for what you've asked for with up to 1024 arguments and without using additional stuff like boost. It defines an EVAL(...) and also a MAP(m, first, ...) macro to do recursion and to use for each iteration the macro m with the next parameter first.

    With the use of that, your apply(...) looks like: #define apply(...) EVAL(MAP(apply_, __VA_ARGS__)).

    It is mostly copied from C Pre-Processor Magic. It is also great explained there. You can also download these helper macros like EVAL(...) at this git repository, there are also a lot of explanation in the actual code. It is variadic so it takes the number of arguments you want.

    But I changed the FIRST and the SECOND macro as it uses a Gnu extension like it is in the source I've copied it from. This is said in the comments below by @HWalters:

    Specifically, 6.10.3p4: "Otherwise [the identifier-list ends in a ...] there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...)".

    Main function part:

    int main()
    {
       int a, b, c;
       apply(a, b, c) /* Expands to: f(a); f(b); f(c); */
    
       return 0;
    }
    

    Macro definitions:

    #define FIRST_(a, ...) a
    #define SECOND_(a, b, ...) b
    
    #define FIRST(...) FIRST_(__VA_ARGS__,)
    #define SECOND(...) SECOND_(__VA_ARGS__,)
    
    #define EMPTY()
    
    #define EVAL(...) EVAL1024(__VA_ARGS__)
    #define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__))
    #define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__))
    #define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__))
    #define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__))
    #define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__))
    #define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__))
    #define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__))
    #define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__))
    #define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__))
    #define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__))
    #define EVAL1(...) __VA_ARGS__
    
    #define DEFER1(m) m EMPTY()
    #define DEFER2(m) m EMPTY EMPTY()()
    
    #define IS_PROBE(...) SECOND(__VA_ARGS__, 0)
    #define PROBE() ~, 1
    
    #define CAT(a,b) a ## b
    
    #define NOT(x) IS_PROBE(CAT(_NOT_, x))
    #define _NOT_0 PROBE()
    
    #define BOOL(x) NOT(NOT(x))
    
    #define IF_ELSE(condition) _IF_ELSE(BOOL(condition))
    #define _IF_ELSE(condition) CAT(_IF_, condition)
    
    #define _IF_1(...) __VA_ARGS__ _IF_1_ELSE
    #define _IF_0(...)             _IF_0_ELSE
    
    #define _IF_1_ELSE(...)
    #define _IF_0_ELSE(...) __VA_ARGS__
    
    #define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)())
    #define _END_OF_ARGUMENTS_() 0
    
    #define MAP(m, first, ...)           \
      m(first)                           \
      IF_ELSE(HAS_ARGS(__VA_ARGS__))(    \
        DEFER2(_MAP)()(m, __VA_ARGS__)   \
      )(                                 \
        /* Do nothing, just terminate */ \
      )
    #define _MAP() MAP
    
    #define apply_(x) f(x);
    #define apply(...) EVAL(MAP(apply_, __VA_ARGS__))
    

    To test macro expansion it is useful to use gcc with the command line argument -E:

    $ gcc -E srcFile.c
    

    because your're getting concrete error messages and understand what's going on.

    0 讨论(0)
  • 2020-12-19 23:37

    Everything is possible in C if you throw enough ugly macros at it. For example, you can have an ugly function-like macro:

    #include <stdio.h>
    
    int f (int a)
    {
      printf("%d\n", a);
    }
    
    #define SIZEOF(arr) (sizeof(arr) / sizeof(*arr))
    
    #define apply(...)                    \
    {                                     \
      int arr[] = {__VA_ARGS__};          \
      for(size_t i=0; i<SIZEOF(arr); i++) \
      {                                   \
        f(arr[i]);                        \
      }                                   \
    }
    
    int main (void)
    {
      apply(1, 2, 3);
    }
    

    Notice that 1) This would be much better off as a variadic function, and 2) it would be even better if you get rid of the variadic nonsense entirely and simply make a function such as

    int f (size_t n, int array[n])
    
    0 讨论(0)
  • 2020-12-19 23:37

    IMO impossible unless you use horrible tricks and hacks like this

    https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s

    And as I see it calls the same function so the type of the parameter is known - why you do not use stdarg instead.

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