Pavel P
Pavel P

Reputation: 16843

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.

Upvotes: 10

Views: 14397

Answers (3)

Andrew
Andrew

Reputation: 1

I really struggled to understand this sequence iteration concept, but piecing together the above answer and the answer here - and, working my way through it bit by bit... - I believe I have figured it out, and how to explain and understand it.


Here is some friendly code, which I have tweaked by:

  • renaming the macros (I find that naming the macros a little more sensibly helps me to understand the process a lot better)
  • giving an optional empty parameters option if wanted via. __VA_OPT__ (that part can be removed if unwanted)
  • rearranging the order of the macros so they work with my current compiler, g++ (Technically this code is c++, not c, but it's basically the same thing here.)
  • providing the ability to swap out which variadic macro is utilized for this recursion process

Note that the parameters have to be specified in the format (() () ()) instead of ( , , ), because the function-style macros rely upon it in order to work with any parameters. See below the code for an in-depth explanatory walkthrough.

#include <iostream>

#define CONCAT(a, ...) a ## __VA_ARGS__
#define CONCAT_FUNC(a, ...) CONCAT(a, __VA_ARGS__)

//Whatever is inside each () will be prepended and appended with what's here.
#define MyVariadicMacro(...) << __VA_ARGS__ + 7 << " "
#define MyVariadicMacro2(...) << __VA_ARGS__ << " "

#define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
#define RESOLVE_B(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_A
#define RESOLVE_A_END
#define RESOLVE_B_END
#define RECURSE(...) CONCAT_FUNC(RESOLVE_A __VA_ARGS__, _END)

int main()
{
  //Choose your own variadic macro, to provide what to prepend and append to each variadic argument!
  #define VARIADIC_FUNC MyVariadicMacro

  //Note: Empty ()'s are in here just to provide an example that they can be ignored via. __VA_OPT__().
  std::cout RECURSE(() (0) () (1) (2) (3) ());

  //Swapping out with variadic macro is being utilized for RECURSE.
  #undef VARIADIC_FUNC
  #define VARIADIC_FUNC MyVariadicMacro2

  std::cout RECURSE(() (0) () (1) (2) (3) ());

  #undef VARIADIC_FUNC

  return 0;
}

Output: 7 8 9 10 0 1 2 3

//Starting with:
std::cout RECURSE(() (0) () (1) (2) (3) ());

//Apply: #define RECURSE(...) CONCAT_FUNC(RESOLVE_A __VA_ARGS__, _END)
std::cout CONCAT_FUNC(RESOLVE_A() (0) () (1) (2) (3) (), _END);

//Apply: #define CONCAT_FUNC(a, ...) CONCAT(a, __VA_ARGS__)
std::cout CONCAT(RESOLVE_A() (0) () (1) (2) (3) (), _END);

//Apply: #define CONCAT(a, ...) a ## __VA_ARGS__
std::cout RESOLVE_A() (0) () (1) (2) (3) () ## _END;

//Apply: #define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
//Note: Since the () is empty, the __VA_OPT__() part is simply skipped.
std::cout RESOLVE_B(0) () (1) (2) (3) () ## _END;

//Apply: #define RESOLVE_B(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_A
std::cout VARIADIC_FUNC(0) RESOLVE_A() (1) (2) (3) () ## _END;

//Apply: #define MyVariadicMacro(...) << __VA_ARGS__ + 7 << " "
//Apply: #define VARIADIC_FUNC MyVariadicMacro
std::cout << 0 + 7 << " " RESOLVE_A() (1) (2) (3) () ## _END;

//Apply: #define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
//Note: Since the () is empty, the __VA_OPT__() part is simply skipped.
std::cout << 0 + 7 << " " RESOLVE_B(1) (2) (3) () ## _END;

//And so on... ending up with:
//Note: Ending with empty () or non-empty() doesn't matter; either way, we will end up with a RESOLVE_?_END.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " " RESOLVE_A() ## _END;

//Apply: #define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
//Note: Since the () is empty, the __VA_OPT__() part is simply skipped.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " " RESOLVE_B ## _END;

//Apply: ## simply concatenates.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " " RESOLVE_B_END;

//Apply: #define RESOLVE_B_END
//Note: In this particular case, we happened to end up with RESOLVE_B_END; in other cases, we will end with
//RESOLVE_A_END.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " ";

//Etc.
std::cout << 7 << " " << 8 << " " << 9 << " " << 10 << " ";

Note: If you want to use RECURSE with an X-macro concept, you have to do something a little extra. The problem with an X-macro is you would have it defined like this for usage in a recursive macro:

#define MyThing (() (0) () (1) (2) (3) ())

and when you go to use it like that, either via. RECURSE() or any other macro, it's wrapped in some extra parentheses:

//It interprets this as RECURSE((() (0) () (1) (2) (3) ())), which is bad.
std::cout RECURSE(MyThing);

The solution is to use a macro like this, and let it naturally resolve to remove the parentheses. Here's an example of modifying RECURSE() for this:

#define ESCAPE_PAREN(...) __VA_ARGS__

//Old RECURSE():
#define RECURSE(...) CONCAT_FUNC(RESOLVE_A __VA_ARGS__, _END)

//New RECURSE():
#define RECURSE(...) CONCAT_FUNC(RESOLVE_A ESCAPE_PAREN __VA_ARGS__, _END)

//Alternatively, just use this instead of RECURSE() (seems to work better):
#define RECURSE_ESCAPE(...) ESCAPE_PAREN __VA_ARGS__

Important to note here is that ESCAPE_PAREN when used does not wrap the __VA_ARGS__ in ().


Edit: I updated the above macros after attempting to use them in a real project. I also added some other related macros that may be of use (RECURSE_FIRST (only go through the first entry), RECURSE_LATTER (only go through the latter entries after the first entry), and their _ESCAPE versions, and RECURSE_SPLIT (go through the first entry, apply the VARIADIC_FUNC_FIRST() macro on that, go through only the latter entries, apply the VARIADIC_FUNC() macro on those, go through the first entry again, apply the VARIADIC_FUNC_END() macro on that, and concatenate all of these together...)):

//#define ESCAPE_PAREN(...) __VA_ARGS__

#define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__) RESOLVE_B)
#define RESOLVE_B(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__) RESOLVE_A)
#define RECURSE(...) RESOLVE_A __VA_ARGS__
//#define RECURSE_ESCAPE(...) RECURSE(ESCAPE_PAREN __VA_ARGS__)

#define RESOLVE_FIRST(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__) DISCARD_A)
#define RESOLVE_LATTER(...) RESOLVE_B
#define DISCARD_A(...) __VA_OPT__(DISCARD_B)
#define DISCARD_B(...) __VA_OPT__(DISCARD_A)
#define RECURSE_FIRST(...) RESOLVE_FIRST __VA_ARGS__ ()
#define RECURSE_LATTER(...) RESOLVE_LATTER __VA_ARGS__ ()

#define RESOLVE_SPLIT_FIRST(...) __VA_OPT__(VARIADIC_FUNC_FIRST(__VA_ARGS__) DISCARD_A)
#define RESOLVE_SPLIT_END(...) __VA_OPT__(VARIADIC_FUNC_END(__VA_ARGS__) DISCARD_A)
#define RECURSE_SPLIT(...) RESOLVE_SPLIT_FIRST __VA_ARGS__ () RESOLVE_LATTER __VA_ARGS__ () RESOLVE_SPLIT_END __VA_ARGS__ ()

These I found to be much less hassle/inhibiting when trying to work with commas and semicolons in my macros... They of course work just like above, and you can use X-macros in the following form with them:

MyXMacro((a) (b) (57) (32))
MyXMacro((c) (d) (49) (32))

See also:

Upvotes: 1

Paul Fultz II
Paul Fultz II

Reputation: 18190

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).

Upvotes: 10

Pavel P
Pavel P

Reputation: 16843

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

Upvotes: 4

Related Questions