5
votes

I'd like to append a stringified macro argument to each element in a variadic macro. I think I know what I need, but I couldn't come up with a working solution just yet. Given a variadic macro like:

#define FIELD_DECLARATION(NAME, OTHER_FIELD, ...)

FIELD_DECLARATION(First, Thing)
FIELD_DECLARATION(Second, Thing, Thing, Nothing)

I'd like to generate:

field_First = {ThingArg};
field_Second = {ThingArg, ThingArg, NothingArg};

I guess what I need is to recursively keep expanding __VA_ARGS__ until it reaches no elements, and append "Arg" while doing expansion. Finally, pass the result to another variadic macro that produces a comma-separated-list of arguments.

I've tried this, which wouldn't work (and it isn't what I described, either):

#define UNPACK_VA_1(A1) A1 ## Arg
#define UNPACK_VA_2(A1, A2) UNPACK_VA_1(A1), UNPACK_VA_1(A2)
#define UNPACK_VA_3(A1, A2, A3) UNPACK_VA_2(A1, A2), UNPACK_VA_1(A3)
#define UNPACK_VA_4(A1, A2, A3, A4) UNPACK_VA_2(A1, A2), UNPACK_VA_2(A3, A4)
#define UNPACK_VA(...) UNPACK_VA_4(__VA_ARGS__)

#define FOO(x, y, ...) UNPACK_VA(__VA_ARGS__)
FOO(One, Two, Three, Four, Five, Six)

While this somewhat works, I couldn't come up with a scalable solution. It'd be great if someone could shed a light.

2
Have you looked at Boost::Preprocessor? If it can be done, it can be done with that.Jonathan Leffler
This is a C project, so cannot have Boost. However, I might take a closer look and mimic what they do. Good point, thanks! I think I'm looking for something similar to a for-each on variadic arguments, which have examples on SO.user1150609
Boost::Preprocessor is language-neutral between C and C++. And you tagged the question with both C++ and C. Was that a mistake?Jonathan Leffler
Right, I've just removed C++ from the tag list. Since it's the same preprocessor, I thought both C++ and C programmers can see the question. I've never tried Boost.Preprocessor in a C project before, I'll try that shortly. Meanwhile, I've found and tried this answer, with reduced number of expansions. I consider going with that, but I must send this for review first.user1150609
"Meanwhile, I've found and tried (link describing MSVC stuff)" ...wait... are you using MSVC? If so it would help to mention that. MSVC doesn't have a standard preprocessor so the semantics are all different. I typically make a running assumption that "c preprocessor" refers to a standard one (almost every other major compiler is standard)H Walters

2 Answers

10
votes

Here's one scalable approach. First, some general utility macros:

#define EVAL(...) __VA_ARGS__
#define VARCOUNT(...) \
   EVAL(VARCOUNT_I(__VA_ARGS__,9,8,7,6,5,4,3,2,1,))
#define VARCOUNT_I(_,_9,_8,_7,_6,_5,_4,_3,_2,X_,...) X_
#define GLUE(X,Y) GLUE_I(X,Y)
#define GLUE_I(X,Y) X##Y
#define FIRST(...) EVAL(FIRST_I(__VA_ARGS__,))
#define FIRST_I(X,...) X
#define TUPLE_TAIL(...) EVAL(TUPLE_TAIL_I(__VA_ARGS__))
#define TUPLE_TAIL_I(X,...) (__VA_ARGS__)

#define TRANSFORM(NAME_, ARGS_) (GLUE(TRANSFORM_,VARCOUNT ARGS_)(NAME_, ARGS_))
#define TRANSFORM_1(NAME_, ARGS_) NAME_ ARGS_
#define TRANSFORM_2(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_1(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_3(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_2(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_4(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_3(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_5(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_4(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_6(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_5(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_7(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_6(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_8(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_7(NAME_,TUPLE_TAIL ARGS_)
#define TRANSFORM_9(NAME_, ARGS_) NAME_(FIRST ARGS_),TRANSFORM_8(NAME_,TUPLE_TAIL ARGS_)

Semantically, VARCOUNT counts arguments; GLUE is a typical indirect paster; FIRST extracts the first argument; EVAL expands to its arguments (with intent to evaluate), and TUPLE_TAIL returns the tail of a tuple (i.e., it discards the first argument).

TRANSFORM here is the main idea; TRANSFORM(FOO,(X,Y,Z)) takes a tuple (X,Y,Z) to (FOO(X),FOO(Y),FOO(Z)).

This in place, here's the special purpose code:

#define Z_ARG(X) GLUE(X,Arg)
#define MAKE_INITIALIZER(...) { __VA_ARGS__ }
#define FIELD_DECLARATION(FNAME_, ...) \
   GLUE(field_, FNAME_) = EVAL(MAKE_INITIALIZER TRANSFORM(Z_ARG, (__VA_ARGS__)));

Given the above, this should be readable, but just to explain... Z_ARG pastes Arg to an item; MAKE_INITIALIZER transforms a preprocessor tuple to an initialization list; and FIELD_DECLARATION is your macro. Note that EVAL wraps the MAKE_INITIALIZER/transformed tuple so it will actually call that macro.

Note: Moved EVAL to the top and used it in a few more places, such that this will work in MSVC as well.

Demonstration, original code

Demonstration, current code

0
votes

I have tested the code above on Visual studio C++, there are some problem and compiler show errors. some prentices specially for VA_ARGS was not necessary and also somewhere we have to use VA_ARGS and ... instead of 'Args' the correct code and also extended to 60 input item is :

#ifndef __MACROS__
#define __MACROS__
#define EVAL(...) __VA_ARGS__
#define VARCOUNT(...) \
   EVAL(VARCOUNT_I(__VA_ARGS__,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,))
#define VARCOUNT_I(_,_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,X_,...) X_
#define GLUE(X,Y) GLUE_I(X,Y)
#define GLUE_I(X,Y) X##Y
#define FIRST(...) EVAL(FIRST_I(__VA_ARGS__,))
#define FIRST_I(X,Args) X
#define TUPLE_TAIL(...) EVAL(TUPLE_TAIL_I(__VA_ARGS__))
#define TUPLE_TAIL_I(X,...) __VA_ARGS__

#define TRANSFORM(NAME_, ...) GLUE(TRANSFORM_,VARCOUNT(__VA_ARGS__))(NAME_, __VA_ARGS__)
#define TRANSFORM_1(NAME_, ARGS_) NAME_ (ARGS_)
#define TRANSFORM_2(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_1(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_3(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_2(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_4(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_3(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_5(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_4(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_6(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_5(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_7(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_6(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_8(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_7(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_9(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_8(NAME_,TUPLE_TAIL( ARGS_))

#define TRANSFORM_10(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_9(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_11(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_10(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_12(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_11(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_13(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_12(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_14(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_13(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_15(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_14(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_16(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_15(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_17(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_16(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_18(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_17(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_19(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_18(NAME_,TUPLE_TAIL( ARGS_))

#define TRANSFORM_20(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_19(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_21(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_20(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_22(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_21(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_23(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_22(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_24(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_23(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_25(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_24(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_26(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_25(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_27(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_26(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_28(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_27(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_29(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_28(NAME_,TUPLE_TAIL( ARGS_))

#define TRANSFORM_30(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_29(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_31(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_30(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_32(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_31(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_33(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_32(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_34(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_33(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_35(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_34(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_36(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_35(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_37(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_36(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_38(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_37(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_39(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_38(NAME_,TUPLE_TAIL( ARGS_))

#define TRANSFORM_40(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_39(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_41(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_40(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_42(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_41(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_43(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_42(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_44(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_43(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_45(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_44(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_46(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_45(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_47(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_46(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_48(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_47(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_49(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_48(NAME_,TUPLE_TAIL( ARGS_))

#define TRANSFORM_50(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_49(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_51(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_50(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_52(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_51(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_53(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_52(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_54(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_53(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_55(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_54(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_56(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_55(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_57(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_56(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_58(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_57(NAME_,TUPLE_TAIL( ARGS_))
#define TRANSFORM_59(NAME_, ARGS_) NAME_(FIRST (ARGS_)) TRANSFORM_58(NAME_,TUPLE_TAIL( ARGS_))
#endif