Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C-preprocessor recursive macro

#define PP_ARG0_(arg0, ...) arg0
#define PP_REST_(arg0, ...) __VA_ARGS__
#define PP_ARG0(args) PP_ARG0_ args
#define PP_REST(args) PP_REST_ args

#define FUNCTION(name) void name();
#define FUNCTION_TABLE(...)                   \
    FUNCTION(PP_ARG0((__VA_ARGS__)))          \
    FUNCTION_TABLE(PP_REST((__VA_ARGS__)))    \

test code:

FUNCTION_TABLE(f1, f2,f3,testA,testB,testC);

Obviously, because of recursive expansion it will only declare void f1(); and the rest won't be expanded:

void f1(); FUNCTION_TABLE(f2,f3,testA,testB,testC);

What kind of trick can I use to achieve recursive expansion in this case? The problem is that I need to support MANY arguments (up 100) and I absolutely cannot use boost.

like image 971
Pavel P Avatar asked Nov 28 '12 21:11

Pavel P


2 Answers

The simplest solution is to use sequence iteration like this:

#define CAT(x, y) PRIMITIVE_CAT(x, y)
#define PRIMITIVE_CAT(x, y) x ## y

#define FUNCTION(name) void name();
#define FUNCTION_TABLE(seq) CAT(FUNCTION_TABLE_1 seq, _END)
#define FUNCTION_TABLE_1(x) FUNCTION(x) FUNCTION_TABLE_2
#define FUNCTION_TABLE_2(x) FUNCTION(x) FUNCTION_TABLE_1
#define FUNCTION_TABLE_1_END
#define FUNCTION_TABLE_2_END

Then you call FUNCTION_TABLE with a preprocessor sequence instead of varidiac arguments:

FUNCTION_TABLE((f1)(f2)(f3)(testA)(testB)(testC))

Not only is this much simpler, its also faster(ie faster compilation) than using the recursive solution(like the one you've shown or like this one here).

like image 167
Paul Fultz II Avatar answered Nov 08 '22 09:11

Paul Fultz II


Here's the answer in case somebody wants to do the same.

#define _PP_0(_1, ...) _1            // (a,b,c,d) => a
#define _PP_X(_1, ...) (__VA_ARGS__) // (a,b,c,d) => (b,c,d)

//for each a in __VA_ARGS__ do f(a,x) 
//where x is some parameter passed to PP_TRANSFORM
#define PP_TRANSFORM(f,x,...) \
    PP_JOIN(PP_TRANSFORM_,PP_NARG(__VA_ARGS__))(f,x,(__VA_ARGS__))

#define PP_TRANSFORM_0(...)
#define PP_TRANSFORM_1( f,x,a) f(_PP_0 a,x) PP_TRANSFORM_0( f,x,_PP_X a)
#define PP_TRANSFORM_2( f,x,a) f(_PP_0 a,x) PP_TRANSFORM_1( f,x,_PP_X a)
...
#define PP_TRANSFORM_51(f,x,a) f(_PP_0 a,x) PP_TRANSFORM_50( f,x,_PP_X a)
...
#define PP_TRANSFORM_99(f,x,a) f(_PP_0 a,x) PP_TRANSFORM_98(f,x,_PP_X a)
#define PP_TRANSFORM_100(f,x,a)f(_PP_0 a,x) PP_TRANSFORM_99(f,x,_PP_X a)

where PP_NARG is the macro that counts number of arguments and PP_JOIN is the macro that joins tokens (that is PP_JOIN(a,b) => ab). You'll also need to patch that PP_NARG if you want to be able to process more than 64 arguments.

Now, back to the original question. Solution using the PP_TRANSFORM is:

#define FUNCTION(name, dummy) void name();
#define FUNCTION_TABLE(...) PP_TRANSFORM(FUNCTION,dummy,__VA_ARGS__)

if you want to generate c++ implementation functions then you can use that opaque x parameter of PP_TRANSFORM:

#define FUNCTION_CPP(name, class) void class::name(){}
#define FUNCTION_TABLE_CPP(...) PP_TRANSFORM(FUNCTION_CPP,MyClass,__VA_ARGS__)

All this works equally well with GCC and MSVC preprocessors; PP_TRANSFORM_NN doesn't use __VA_ARGS__ to avoid separate implementations of 100 defines for GCC and MSVC

like image 45
Pavel P Avatar answered Nov 08 '22 08:11

Pavel P