是否有可能遍历variablesmacros中的参数?

我想知道是否有可能迭代传递给在C99 variadicmacros或使用任何GCC扩展的参数?

例如,是否有可能编写一个通用的macros,它采用一个结构和它的字段作为parameter passing,并打印结构中每个字段的偏移量?

像这样的东西:

结构{
     int a;
     int b;
     int c;
 };

 / * PRN_STRUCT_OFFSETS将打印每个字段的偏移量 
   在作为第一个论点的结构之内。
 * /

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

    返回0;
 }

这是我今天的作业,是基于macros观的技​​巧,今天我特别了解了由Laurent Deniau发明的__VA_NARG__ 。 无论如何,为了清晰起见,以下示例代码最多可以处理8个字段。 如果你需要更多的话,只需要扩展代码就可以了(这是因为预处理器没有recursionfunction,因为它只读取一次文件)。

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

打印出来:

 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 

编辑:这是一个稍微不同的版本,试图是更通用的。 FOR_EACH(what, ...)macros对variables参数列表中的每个其他参数应用了what

所以,你只需要定义一个像这样的单个参数的macros:

 #define DO_STUFF(x) foo(x) 

这将被应用到列表中的每个参数。 所以,对于你的典型例子,你需要进行一些修改,但是仍然是简洁的:

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

你这样应用:

 FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); 

最后,一个完整的示例程序:

 #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在上面的回答中使用了“ 重载macros观”参数

用foo.h:

 // Make a FOREACH macro #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__,FE_5,FE_4,FE_3,FE_2,FE_1)(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生成:

 Outer::Next::Inner::MyFoo foo(); namespace Outer {namespace Next {namespace Inner { class Foo; }}} 

如果用X-Macros描述结构,则可以编写函数或macros来遍历结构的所有字段并打印它们的偏移量。

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

也许使用可变参数作为数组初始值设定项,并迭代countof(数组)? 即sizeof(array)/ sizeof(array [0])。 该数组可能可能是一个C99匿名数组。

我不能想到另一种方法迭代macros的var-args,因为我不知道如何对每个var-arg元素的文本做任何事情。 var-arg部分也可能是一个逗号的单个参数,你可以用CPP,AFAIK来做。

但这是我的想法迭代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) 

这是我的解决scheme
请享用

 #include <stdlib.h> #include <stdio.h> #define ITERATE_OVER_VARADICT_MACROS( str, ...)\ do{\ int i, _arr_[] = {__VA_ARGS__};\ fprintf(stderr,"msg =%s\n", str); \ for(i=0; i<sizeof(_arr_)/sizeof(int) ; i++){ \ fprintf(stderr,"_arr_[%d]= %d\n", i, _arr_[i] ); \ }\ }while(0) int main(int argc, char* argv[]) { ITERATE_OVER_VARADICT_MACROS("Test of iterate over arguments in variadic macros", 10,12, 34); return 0; } 

Gregory Pakosz的解决scheme效果很好。 但是我有两个小问题:

  1. 用迂回选项编译我得到了警告:“ISO99需要使用其余参数”。 这是由第一个FOR_EACH_1macros中的variad参数引起的。 删除这些,并将FOR_EACH_2中的呼叫更改为FOR_EACH_1删除了此警告。

     #define FOR_EACH_1(what, x) #define FOR_EACH_2(what, x, ...)\ what(x); \ FOR_EACH_1(what); 
  2. 因为我以一种非常通用的方式使用它,所以有时我不得不用一个参数来调用重复macros。 (我知道重复一个项目是没有意义的);)。 幸运的是,解决这个问题非常简单。 只需从FOR_EACHmacros中删除x参数。

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

这里有两个更改的完整列表:

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

这是我能想到的最好的标准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; } 

我将这添加为另一个答案。 这里试着用C ++ 0x来完成,用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; } 

程序打印

123

但是,使用这种方法,传递给lambdaexpression式的所有参数都需要具有相同的types,在上面的示例中为int 。 不过,lambdas允许你捕获variables,如:

 int main() { int offset = 10; for_each([offset] (int x) { cout << offset + x << endl; }, 1, 2, 3); return 0; } 

打印出来:

 11 12 13 

如果您的目标是Objective-C …请查看Github上的AWESOME KSVarArgs

KSVarArgs是一组devise用于在Objective-C中处理variables参数的macros。 所有macros都假定可变参数列表仅包含objective-c对象或类似对象的结构(可分配给idtypes)。 基本macrosksva_iterate_list()迭代variables参数,为每个参数调用一个块,直到它遇到一个终止的nil。 其他的macros是为了方便转换到普通的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); }]; }]; } 

示例用法:

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