92

I was wondering if it is possible to iterate over arguments passed to a variadic macro in C99 or using any GCC extensions ?

For e.g. is it possible to write a generic macro that takes a structure and its fields passed as arguments and prints offset of each field within the structure ?

Something like this:

struct a {
    int a;
    int b;
    int c;
};

/* PRN_STRUCT_OFFSETS will print offset of each of the fields 
   within structure passed as the first argument.
*/

int main(int argc, char *argv[])
{
    PRN_STRUCT_OFFSETS(struct a, a, b, c);

    return 0;
}
a3f
  • 8,517
  • 1
  • 41
  • 46
vshenoy
  • 1,153
  • 1
  • 8
  • 14

10 Answers10

82

Here is my homework of the day, it's based on macro tricks and today I particularly learnt about __VA_NARG__ invented by Laurent Deniau. Anyway, the following sample code works up to 8 fields for the sake of clarity. Just extend the code by duplicating if you need more (this is because the preprocessor doesn't feature recursion, as it reads the file only once).

#include <stdio.h>
#include <stddef.h>

struct a
{
  int a;
  int b;
  int c;
};

struct b
{
  int a;
  int b;
  int c;
  int d;
};

#define STRINGIZE(arg)  STRINGIZE1(arg)
#define STRINGIZE1(arg) STRINGIZE2(arg)
#define STRINGIZE2(arg) #arg

#define CONCATENATE(arg1, arg2)   CONCATENATE1(arg1, arg2)
#define CONCATENATE1(arg1, arg2)  CONCATENATE2(arg1, arg2)
#define CONCATENATE2(arg1, arg2)  arg1##arg2

/* PRN_STRUCT_OFFSETS will print offset of each of the fields 
 within structure passed as the first argument.
 */
#define PRN_STRUCT_OFFSETS_1(structure, field, ...) printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));
#define PRN_STRUCT_OFFSETS_2(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_1(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_3(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_2(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_4(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_3(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_5(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
 PRN_STRUCT_OFFSETS_4(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_6(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_5(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_7(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_6(structure, __VA_ARGS__)
#define PRN_STRUCT_OFFSETS_8(structure, field, ...)\
  printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\
  PRN_STRUCT_OFFSETS_7(structure, __VA_ARGS__)

#define PRN_STRUCT_OFFSETS_NARG(...) PRN_STRUCT_OFFSETS_NARG_(__VA_ARGS__, PRN_STRUCT_OFFSETS_RSEQ_N())
#define PRN_STRUCT_OFFSETS_NARG_(...) PRN_STRUCT_OFFSETS_ARG_N(__VA_ARGS__) 
#define PRN_STRUCT_OFFSETS_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N 
#define PRN_STRUCT_OFFSETS_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0

#define PRN_STRUCT_OFFSETS_(N, structure, field, ...) CONCATENATE(PRN_STRUCT_OFFSETS_, N)(structure, field, __VA_ARGS__)

#define PRN_STRUCT_OFFSETS(structure, field, ...) PRN_STRUCT_OFFSETS_(PRN_STRUCT_OFFSETS_NARG(field, __VA_ARGS__), structure, field, __VA_ARGS__)

int main(int argc, char *argv[])
{
  PRN_STRUCT_OFFSETS(struct a, a, b, c);
  printf("\n");
  PRN_STRUCT_OFFSETS(struct b, a, b, c, d);

  return 0;
}

which prints out:

struct a:a-0
struct a:b-4
struct a:c-8

struct b:a-0
struct b:b-4
struct b:c-8
struct b:d-12

EDIT: Here is a slightly different version that tries to be more generic. The FOR_EACH(what, ...) macro applies what to every other argument in the variable argument list.

So, you just have to define a macro that takes a single argument like this:

#define DO_STUFF(x) foo(x)

which is going to be applied to every argument in the list. So, for your typical example you need to hack a bit but it still remains concise:

#define PRN_STRUCT_OFFSETS_(structure, field) printf(STRINGIZE(structure)":"STRINGIZE(field)" - offset = %d\n", offsetof(structure, field));
#define PRN_STRUCT_OFFSETS(field) PRN_STRUCT_OFFSETS_(struct a, field)

And you apply it like this:

FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c);

Finally, a complete sample program:

#include <stdio.h>
#include <stddef.h>

struct a
{
  int a;
  int b;
  int c;
};

#define STRINGIZE(arg)  STRINGIZE1(arg)
#define STRINGIZE1(arg) STRINGIZE2(arg)
#define STRINGIZE2(arg) #arg

#define CONCATENATE(arg1, arg2)   CONCATENATE1(arg1, arg2)
#define CONCATENATE1(arg1, arg2)  CONCATENATE2(arg1, arg2)
#define CONCATENATE2(arg1, arg2)  arg1##arg2

#define FOR_EACH_1(what, x, ...) what(x)
#define FOR_EACH_2(what, x, ...)\
  what(x);\
  FOR_EACH_1(what,  __VA_ARGS__);
#define FOR_EACH_3(what, x, ...)\
  what(x);\
  FOR_EACH_2(what, __VA_ARGS__);
#define FOR_EACH_4(what, x, ...)\
  what(x);\
  FOR_EACH_3(what,  __VA_ARGS__);
#define FOR_EACH_5(what, x, ...)\
  what(x);\
 FOR_EACH_4(what,  __VA_ARGS__);
#define FOR_EACH_6(what, x, ...)\
  what(x);\
  FOR_EACH_5(what,  __VA_ARGS__);
#define FOR_EACH_7(what, x, ...)\
  what(x);\
  FOR_EACH_6(what,  __VA_ARGS__);
#define FOR_EACH_8(what, x, ...)\
  what(x);\
  FOR_EACH_7(what,  __VA_ARGS__);

#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__) 
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N 
#define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0

#define FOR_EACH_(N, what, x, ...) CONCATENATE(FOR_EACH_, N)(what, x, __VA_ARGS__)
#define FOR_EACH(what, x, ...) FOR_EACH_(FOR_EACH_NARG(x, __VA_ARGS__), what, x, __VA_ARGS__)

#define PRN_STRUCT_OFFSETS_(structure, field) printf(STRINGIZE(structure)":"STRINGIZE(field)" - offset = %d\n", offsetof(structure, field));
#define PRN_STRUCT_OFFSETS(field) PRN_STRUCT_OFFSETS_(struct a, field)

int main(int argc, char *argv[])
{
  FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c);
  printf("\n");

  return 0;
}
Gregory Pakosz
  • 69,011
  • 20
  • 139
  • 164
  • 2
    Neat. I was wondering whether it would be possible to split up a __VA_ARGS__ by passing it to another macro that had a named argument to catch one of the __VA_ARGS__, so I liked this answer. Too bad CPP makes you write macros for each count, instead of allowing recursive expansion and doing something different when there are no args left. I don't know if I'd ever include that big a collection of macros unless it was going to save a lot of code somewhere. Well, maybe for my own use during development... Anyway, neat trick. – Peter Cordes Dec 09 '09 at 21:03
  • 1
    That's a nice trick Gregory. I had stumbled upon the __VA_NARG__ post when googling, but didn't know (or was ignorant) that you could use it to build a dispatcher macro based on number of arguments. GMan, yours was the approach I had originally taken. phillipe, X-Macros is an interesting approach. Thanks to all of you guys for your responses. – vshenoy Dec 10 '09 at 05:48
  • 2
    I've seen how the double-stringize works in http://stackoverflow.com/questions/2751870/how-exactly-does-the-double-stringize-trick-work, but why are STRINGIZE and CONCATENATE three calls deep? – Henk Feb 09 '11 at 05:26
  • Henk > in fact i don't remember why, it's been in my codebase for ages. 1) either it was needed by some creepy compiler 2) either it's a mistake from my part :) – Gregory Pakosz Feb 09 '11 at 19:54
  • it surely has to do with old code warrior compiler or msvc++ 6 – Gregory Pakosz Feb 10 '11 at 09:00
  • @Henk actually you need a depth of three is there so that you can `CONCATENATE(PREFIX_, FOO(something)` where `FOO` is another macro. If `FOO(something)` evaluates to `bar` (not necessarily a string) then you'll get `PREFIX_bar` – Gregory Pakosz Mar 01 '13 at 10:59
  • @GregoryPakosz this stuff doesn't seem available on msvc? I'm getting a warning and an error on the `FOR_EACH` part `warning C4003: not enough actual parameters for macro 'FOR_EACH_ARG_N'` `error C3861: 'FOR_EACH_': identifier not found` any idea? – vexe Sep 10 '15 at 14:57
  • I for sure ran that with MSVC 2012 and later versions -- I don't have access to MSVC right now though – Gregory Pakosz Sep 10 '15 at 15:16
  • Will it not be a problem if there is only ONE variadic argument? In my case it causes it to be two argument, the one provided + one empty – netdigger Feb 19 '16 at 10:34
  • @vexe There is a problem with __VA_ARGS__ on MSVC. Had to add token expansion in PRN_STRUCT_OFFSETS_NARG_. More info at http://stackoverflow.com/questions/5134523/msvc-doesnt-expand-va-args-correctly – UfoXp Jul 31 '16 at 11:13
  • Latest gcc in conforming mode: `error: ISO C99 requires at least one argument for the "..." in a variadic macro | FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c);`. So the answer should make it clearer that it isn't using standard C. These kind of macros usually involve the non-standard gcc extension `##__VA_ARGS__` to solve this reported problem. – Lundin May 12 '22 at 06:30
62

At the risk of earning an archaeologist badge, I think there is a minor improvement to Gregory's answer above using the technique from Overloading Macro on Number of Arguments

With foo.h:

// Make a FOREACH macro
#define FE_0(WHAT)
#define FE_1(WHAT, X) WHAT(X) 
#define FE_2(WHAT, X, ...) WHAT(X)FE_1(WHAT, __VA_ARGS__)
#define FE_3(WHAT, X, ...) WHAT(X)FE_2(WHAT, __VA_ARGS__)
#define FE_4(WHAT, X, ...) WHAT(X)FE_3(WHAT, __VA_ARGS__)
#define FE_5(WHAT, X, ...) WHAT(X)FE_4(WHAT, __VA_ARGS__)
//... repeat as needed

#define GET_MACRO(_0,_1,_2,_3,_4,_5,NAME,...) NAME 
#define FOR_EACH(action,...) \
  GET_MACRO(_0,__VA_ARGS__,FE_5,FE_4,FE_3,FE_2,FE_1,FE_0)(action,__VA_ARGS__)

// Example
// Some actions
#define QUALIFIER(X) X::
#define OPEN_NS(X)   namespace X {
#define CLOSE_NS(X)  }
// Helper function
#define QUALIFIED(NAME,...) FOR_EACH(QUALIFIER,__VA_ARGS__)NAME

// Emit some code
QUALIFIED(MyFoo,Outer,Next,Inner)  foo();

FOR_EACH(OPEN_NS,Outer,Next,Inner)
  class Foo;
FOR_EACH(CLOSE_NS,Outer,Next,Inner)

cpp foo.h generates:

Outer::Next::Inner::MyFoo foo();

namespace Outer {namespace Next {namespace Inner {
   class Foo;
}}}
fuzzyTew
  • 3,511
  • 29
  • 24
Marvin
  • 2,537
  • 24
  • 35
  • 3
    I needed to change the definition of `GET_MACRO` to `GET_MACRO(__VA_ARGS__,FE_5,FE_4,FE_3,FE_2,FE_1,)(action,__VA_ARGS__)`. Notice the extra comma. Without this, applying the macro to a list with a single argument, I receive `warning: ISO C99 requires rest arguments to be used`. Other than that, great macro! – wyer33 Mar 03 '15 at 21:08
  • That's just great, you deserve an archaeologist badge! – p8me Jul 09 '16 at 19:15
  • 6
    For those attempting this with msvc (2015 here), this needs to be slightly modified since msvc does not expand `__VA_ARGS__` into multiple arguments i.e. when `__VA_ARGS__` is `a,b,c`, `FOO(X, __VA_ARGS__)` becomes `FOO(X, (a,b,c))` instead of `FOO(X, a, b, c)`. The solution is here: http://stackoverflow.com/questions/5134523/msvc-doesnt-expand-va-args-correctly - in short, `GET_MACRO(__VA_ARGS__, ...)(action,__VA_ARGS__)` needs to be rewriten as `EXPAND(GET_MACRO(__VA_ARGS__, ...)(action,__VA_ARGS__))` and the `FE_X` need to be wrapped in an `EXPAND(...)` macro as well. – GaspardP Dec 20 '16 at 15:08
  • 2
    In the `GET_MACRO` invocation, add a comma after `FE_1` to prevent `Wgnu-zero-variadic-macro-arguments` if the macro is only called with 1 item to iterate over. – Etan Jun 01 '17 at 09:55
21

If your structure is described with X-Macros, then it is possible to write a function, or a macro to iterate over all the fields of the structure and print their offset.

#include <stddef.h>   // offsetof macro

//--- first describe the structure, the fields, their types
#define X_FIELDS \
    X(int,    field1) \
    X(int,    field2) \
    X(char,   field3) \
    X(char *, field4)

//--- define the structure, the X macro will be expanded once per field
typedef struct {
#define X(type, name) type name;
    X_FIELDS
#undef X
} mystruct;

//--- "iterate" over all fields of the structure and print out their offset
void print_offset(mystruct *aStruct)
{
#define X(type, name) printf("offset of %s is %d\n", #name, offsetof(mystruct, name));
        X_FIELDS
#undef X
}

//--- demonstrate
int main(int ac, char**av)
{
    mystruct a = { 0, 1, 'a', "hello"};
    print_offset(&a);

    return 0;
}
malat
  • 12,152
  • 13
  • 89
  • 158
philant
  • 34,748
  • 11
  • 69
  • 112
  • 3
    It only obfuscates the declaration of the structure and the function that prints the offsets, but not that much when you know the effect of the X() macro. But the advantage is that when you have to add a new field to the structure, you only have one place to modify, the X_FIELDS macro. Recompile and the print_offset() function will print the offset of the new field. Don't Repeat yourself! – philant Dec 09 '09 at 11:50
  • and only applies if the structure is yours and you are willing to clutter (imho) its definition – Gregory Pakosz Dec 09 '09 at 12:14
  • 4
    I just used this approach in a case in which I wanted to have an enum and have access to the enum elements by name. It indeed obfuscates the code but makes the final user experience better and without constraints. – Dario Aug 30 '13 at 20:18
  • I did something similar, but with a parameter-less Y macro after all but the last X, to allow for the fact that in some contexts it's necessary to have a separator between items rather than a terminator after each. – supercat Dec 04 '15 at 20:12
  • I had no idea this technique had a name and a Wikipedia page! I use X-macros way too often! – Indiana Kernick Oct 07 '18 at 07:35
9

The solution of Gregory Pakosz worked great. But I had two minor problems with it:

  1. Compiling with the pedantic option I got the warning: "ISO99 requires rest arguments to be used". This is caused by the variad arguments in the first FOR_EACH_1 macro. Removing those and changing the call to FOR_EACH_1 in FOR_EACH_2 removed this warning.

    #define FOR_EACH_1(what, x) 
    #define FOR_EACH_2(what, x, ...)\
        what(x);                    \
        FOR_EACH_1(what);
    
  2. Since i used it in a very generic way, i sometimes had to call the repeat macro with only 1 argument. (I know it does not make sense to repeat an item 1 times ;)). Fortunately the solution to this problem was quite simple. Just removing the x parameter from the FOR_EACH macro.

    #define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__)
    

Here the complete listing with the two changes:

#define CONCATENATE(arg1, arg2)   CONCATENATE1(arg1, arg2)
#define CONCATENATE1(arg1, arg2)  CONCATENATE2(arg1, arg2)
#define CONCATENATE2(arg1, arg2)  arg1##arg2

#define FOR_EACH_1(what, x)         \
    what(x)

#define FOR_EACH_2(what, x, ...)    \
    what(x);                        \
    FOR_EACH_1(what, __VA_ARGS__);

#define FOR_EACH_3(what, x, ...)    \
    what(x);                        \
    FOR_EACH_2(what, __VA_ARGS__);

#define FOR_EACH_4(what, x, ...)    \
    what(x);                        \
    FOR_EACH_3(what,  __VA_ARGS__);

#define FOR_EACH_5(what, x, ...)    \
    what(x);                        \
    FOR_EACH_4(what,  __VA_ARGS__);

#define FOR_EACH_6(what, x, ...)    \
  what(x);                          \
  FOR_EACH_5(what,  __VA_ARGS__);

#define FOR_EACH_7(what, x, ...)    \
    what(x);                        \
    FOR_EACH_6(what,  __VA_ARGS__);

#define FOR_EACH_8(what, x, ...)    \
    what(x);                        \
    FOR_EACH_7(what,  __VA_ARGS__);

#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__) 
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N 
#define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0

#define FOR_EACH_(N, what, ...) CONCATENATE(FOR_EACH_, N)(what, __VA_ARGS__)
#define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__)
Iqon
  • 1,920
  • 12
  • 20
5

Maybe use the varargs as an array initializer, and iterate over countof(array)? i.e. sizeof(array)/sizeof(array[0]). The array could potentially be a C99 anonymous array.

I can't think of another way to iterate over the var-args of a macro, since I don't know how to do anything to the text of each var-arg element. The var-arg part might as well be a single argument that has commas in it, for all you can do to it with CPP, AFAIK.

But here's my idea for iterating over var-args:

#define countof(a) ( sizeof(a)/sizeof((a)[0]) )
#define MACRO(fd, format, ...) do { int ar_[] = { __VA_ARGS__ }; \
for(int i=0; i<countof(ar_) ; ++i){ \
    fprintf(fd, format, ar_[i]); \
} } while(0)
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    I'm sorry but I fail to see how this snippet answers the question. First the code misses a definition of `countof` although you give it in the first paragraph. Then it should be `int ar_[]`. Finally it would only work when invoking the macro with a variable argument list of `int` arguments; like this `MACRO(stdout, "%d", 1, 2, 3)` – Gregory Pakosz Dec 09 '09 at 15:31
  • 1
    Obviously you need to adjust the macro to fit your situation. You could make the type one of the macro params. Thanks for catching the missing [], though. – Peter Cordes Dec 09 '09 at 18:31
  • 2
    still, the use of this array means 2 things: all the arguments passed through the variable argument list need to be of the same type (in your case `int`) and must have a public copy constructor – Gregory Pakosz Dec 09 '09 at 18:48
  • Yes, there are major limitations to this. I'm not claiming it's a good or generally useful answer! It may be the best you can do in C99 / GNU C, though. In C++, could you do something with templates instead? – Peter Cordes Dec 09 '09 at 18:56
  • I didn't try with templates yet but I edited my answer trying to be more generic – Gregory Pakosz Dec 09 '09 at 20:03
  • 1
    see http://stackoverflow.com/questions/1872220/is-it-possible-to-iterate-over-arguments-in-variadic-macros/1880292#1880292 for a c++0x version – Gregory Pakosz Dec 10 '09 at 11:11
0

This is the best I can think of, with standard C:

#include <stddef.h>
#include <stdio.h>

// prints a single offset
#define PRN_STRUCT_OFFSET(x, a) printf("&" #x "." #a " = %d\n", offsetof(x, a));

// prints a struct with one member
#define PRN_STRUCT_OFFSETS_1(x, a) PRN_STRUCT_OFFSET(x, a)

// prints a struct with two members
#define PRN_STRUCT_OFFSETS_2(x, a, b) \
            PRN_STRUCT_OFFSET(x, a) \
            PRN_STRUCT_OFFSET(x, b)

// and so on until some N.
// Boost.Preprocessor might help here, I'm not sure

struct some_struct
{
    int a;
    void* c;
};

int main(void)
{
    PRN_STRUCT_OFFSETS_2(struct some_struct, a, c);

    return 0;
}
GManNickG
  • 494,350
  • 52
  • 494
  • 543
0

I'm adding this as another answer. Here is a try at doing it with C++0x, compiled with g++ 4.5.0

#include <iostream>
using namespace std;

template<typename L>
inline void for_each(L l)
{
}

template<typename L, typename P, typename... Q>
inline void for_each(L l, P arg, Q... args)
{
  l(arg);
  for_each(l, args...);
}

int main()
{
  for_each([] (int x) { cout << x; }, 1, 2, 3);

  return 0;
}

The program prints

123

However, with this approach, all the parameters you pass to the lambda expression need to have the same type, int in the example above. However, lambdas allow you to capture variables like:

int main()
{
  int offset = 10;

  for_each([offset] (int x) { cout << offset + x << endl; }, 1, 2, 3);

  return 0;
}

which prints out:

11
12
13
Gregory Pakosz
  • 69,011
  • 20
  • 139
  • 164
0

To enable for an empty __VA_ARGS__, one can use the GNU extension ##_VA_ARGS__ https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

0

As https://stackoverflow.com/a/11994395/1938348 do not work well in every case for GCC 12, there is improved version using __VA_OPT__ to remove superfluous commas:

#include <assert.h>

// Make a FOREACH macro
#define FE_0(WHAT)
#define FE_1(WHAT, X) WHAT(X) 
#define FE_2(WHAT, X, ...) WHAT(X)FE_1(WHAT, __VA_ARGS__)
#define FE_3(WHAT, X, ...) WHAT(X)FE_2(WHAT, __VA_ARGS__)
#define FE_4(WHAT, X, ...) WHAT(X)FE_3(WHAT, __VA_ARGS__)
#define FE_5(WHAT, X, ...) WHAT(X)FE_4(WHAT, __VA_ARGS__)
//... repeat as needed

#define GET_MACRO(_1,_2,_3,_4,_5,NAME,...) NAME 
#define FOR_EACH(action,...) \
  GET_MACRO(__VA_ARGS__ __VA_OPT__(,) FE_5,FE_4,FE_3,FE_2,FE_1,FE_0)(action __VA_OPT__(,) __VA_ARGS__)




#define F(X) _Generic((X), int:1, long: 2, float: 4),


#define CALL(FF, ...) FF((int[]){ FOR_EACH(F, __VA_ARGS__) 0 } __VA_OPT__(,) __VA_ARGS__)

int foo(int* p, ...){ int i = 0; while(*p != 0) { i += *p; ++p; } return i; }

int main()
{
    assert(CALL(foo) == 0);
    
    assert(CALL(foo, 1) == 1);
    assert(CALL(foo, 1l) == 2);
    assert(CALL(foo, 1.f) == 4);

    assert(CALL(foo, 1.f, 1) == 5);
    assert(CALL(foo, 3l, 1.f, 1) == 7);
}

Working example: https://godbolt.org/z/q7nbj7PxY

Yankes
  • 1,958
  • 19
  • 20
-2

If you are targeting Objective-C… check out the AWESOME KSVarArgs on Github

KSVarArgs is a set of macros designed to make dealing with variable arguments easier in Objective-C. All macros assume that the varargs list contains only objective-c objects or object-like structures (assignable to type id). The base macro ksva_iterate_list() iterates over the variable arguments, invoking a block for each argument, until it encounters a terminating nil. The other macros are for convenience when converting to common collections.

/*! @param firstNote NSString that is the only known arg 
 */

- (void) observeWithBlocks:(NSString*)firstNote,...{

  /*! ksva_list_to_nsarray puts varargs into 
      new array, `namesAndBlocks` 
   */
  ksva_list_to_nsarray(firstNote, namesAndBlocks);

  /// Split the array into Names and Blocks

  NSArray *names = [namesAndBlocks subArrayWithMembersOfKind:NSString.class],
     *justBlocks = [namesAndBlocks arrayByRemovingObjectsFromArray:names];

  [names eachWithIndex:^(id obj, NSInteger idx) {
     [self observeName:obj usingBlock:^(NSNotification *n) {    
        ((void(^)())justBlocks[idx])(n);
     }];    
  }];
}

example usage:

[NSNotificationCenter.defaultCenter observeWithBlocks: 
  NSViewFrameDidChangeNotification, /// first, named arg
  ^(NSNotification *m){ [self respondToFrameChange]; }, // vararg
  NSTextViewDidChangeSelectionNotification, // vararg
  ^(NSNotification *z){ [z.infoDict[@"textView"] save]; }, // vararg
  nil // must nil-terminate
]; 
Alex Gray
  • 16,007
  • 9
  • 96
  • 118