7

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.

user1150609
  • 347
  • 2
  • 10
  • Have you looked at Boost::Preprocessor? If it can be done, it can be done with that. – Jonathan Leffler Jun 11 '17 at 01:09
  • 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 Jun 11 '17 at 01:20
  • 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 Jun 11 '17 at 01:21
  • 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](https://stackoverflow.com/a/14735113/1150609), with reduced number of expansions. I consider going with that, but I must send this for review first. – user1150609 Jun 11 '17 at 01:36
  • "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 Jun 11 '17 at 02:02
  • No, we don't use MSVC at the moment. We use gcc and clang. But that example worked in clang (tried on [Compiler Explorer](http://gcc.godbolt.org). If we start using MSVC, I believe it'll be the latest version, and I hope they've got closer to other preprocessors. To best of my knowledge, these bits of preprocessor isn't fully covered by the standard, so most of we do rely on compiler extensions. – user1150609 Jun 11 '17 at 02:07
  • @user1150609 Nope; to my knowledge, the latest MSVC is also non-standard. But in the interest of potential portability I changed my answer a bit. This approach should work with both. – H Walters Jun 11 '17 at 02:20

2 Answers2

13

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

H Walters
  • 2,634
  • 1
  • 11
  • 13
  • This is awesome, thanks! I'll try to hide `TRANSFORM_N` macros somewhere in another header file. I'll have to understand how this expands exactly. In my attempt, I couldn't come up with a pattern matching macro, so I had to pass exactly N arguments to my macro. However, this `field` thing can have at least 3 arguments (so upper-bound is unknown, and that's what I'm trying to create). I'll have to study parts of your example in detail to understand the expansion better. – user1150609 Jun 11 '17 at 02:02
  • I'd love to see a non-scalable solution LOL. +1 though – niCk cAMel Sep 06 '18 at 12:38
  • What is the purpose of the EVAL macro here? – bremen_matt Nov 19 '19 at 20:04
  • @bremen_matt It's an indirection step specifically for [MS's preprocessor (godbolt)](https://godbolt.org/z/V7_Ecy) which needs extra help due to the way it processes `__VA_ARGS__` (namely, treats as a single argument even if the expansion has commas in it). – H Walters Nov 20 '19 at 04:52
  • 1
    Man. Macros... Anyway, for my own personal reference, here is a cleaned up version that I think is a bit clearer. You do still need the EVAL macro once for reasons which I honestly do not care to investigate: https://godbolt.org/z/jD2BHp – bremen_matt Nov 20 '19 at 07:19
1

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