可以在可变宏中迭代参数吗?

我想知道是否可以在 C99中迭代传递给可变宏的参数或者使用任何 GCC 扩展?

例如,有没有可能编写一个泛型宏,它接受一个结构及其作为参数传递的字段,并打印结构中每个字段的偏移量?

就像这样:

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;
}
67767 次浏览

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;
}

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;
}

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)

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;
}

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

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;
}}}

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
];

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

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

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