我想知道是否有可能遍历传递给C99中可变参数宏的参数或使用任何GCC扩展?
例如,是否可以编写一个通用宏,该宏将结构及其字段作为参数传递并打印结构中每个字段的偏移量?
像这样:
构造一个{ 诠释 int b; int c; }; / * PRN_STRUCT_OFFSETS将打印每个字段的偏移量 内部结构作为第一个参数传递。 * / int main(int argc,char * argv []) { PRN_STRUCT_OFFSETS(结构a,a,b,c); 返回0; }
Answers:
这是我今天的作业,它基于宏技巧,今天我特别了解__VA_NARG__
Laurent Deniau发明的内容。无论如何,以下示例代码为清楚起见最多可处理8个字段。如果需要更多,只需通过复制来扩展代码(这是因为预处理器不具有递归功能,因为它只读取一次文件)。
#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, ...)
宏适用what
于变量参数列表中的所有其他参数。
因此,您只需要定义一个采用单个参数的宏,如下所示:
#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;
}
冒着获得考古学家徽章的风险,我认为使用重载论据数量上的宏技术可以对上述格雷戈里的答案进行较小的改进
使用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生成:
Outer::Next::Inner::MyFoo foo();
namespace Outer {namespace Next {namespace Inner {
class Foo;
}}}
GET_MACRO
为GET_MACRO(__VA_ARGS__,FE_5,FE_4,FE_3,FE_2,FE_1,)(action,__VA_ARGS__)
。请注意多余的逗号。没有这个,我将宏应用于具有单个参数的列表warning: ISO C99 requires rest arguments to be used
。除此之外,宏很棒!
__VA_ARGS__
为多个参数,即__VA_ARGS__
is是a,b,c
,FOO(X, __VA_ARGS__)
而FOO(X, (a,b,c))
不是,因此需要稍作修改FOO(X, a, b, c)
。解决方案在这里:stackoverflow.com/questions/5134523/…-简而言之,GET_MACRO(__VA_ARGS__, ...)(action,__VA_ARGS__)
需要重新编写,EXPAND(GET_MACRO(__VA_ARGS__, ...)(action,__VA_ARGS__))
并且还FE_X
需要包装在EXPAND(...)
宏中。
GET_MACRO
调用中,请在后面添加一个逗号,FE_1
以防止Wgnu-zero-variadic-macro-arguments
仅用1项调用该宏来进行迭代。
如果使用X-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;
}
Gregory Pakosz的解决方案效果很好。但是我有两个小问题:
使用pedantic选项进行编译时,出现了警告:“ ISO99要求使用rest参数”。这是由第一个FOR_EACH_1宏中的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);
由于我以一种非常通用的方式使用它,因此有时我不得不只用1个参数调用repeat宏。(我知道重复一个项目没有意义;)。幸运的是,解决该问题的方法非常简单。只需从FOR_EACH宏中删除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__)
也许使用varargs作为数组初始化器,并遍历countof(array)?即sizeof(array)/ sizeof(array [0])。该阵列可能是C99匿名阵列。
我想不出另一种方法来遍历宏的var-args,因为我不知道如何对每个var-arg元素的文本执行任何操作。var-arg部分也可能是其中包含逗号的单个参数,因为您可以使用CPP,AFIAK对它进行全部处理。
但是这是我遍历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)
countof
虽然您在第一段中给出了定义,但代码缺少定义。那应该是int ar_[]
。最后,它只有在使用带有可变参数列表的int
参数调用宏时才有效;像这样MACRO(stdout, "%d", 1, 2, 3)
int
必须具有相同的类型(在您的情况下),并且必须具有公共副本构造函数
对于标准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;
}
要启用空值__VA_ARGS__
,可以使用GNU扩展名##_VA_ARGS__
https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
我将此添加为另一个答案。这是使用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
但是,使用这种方法,int
在上面的示例中,传递给lambda表达式的所有参数都必须具有相同的类型。但是,lambda允许您捕获变量,例如:
int main()
{
int offset = 10;
for_each([offset] (int x) { cout << offset + x << endl; }, 1, 2, 3);
return 0;
}
输出:
11
12
13
STD(cout, endl, cin)
那么它将扩展为using std::cout; using std::endl; using std::cin;
,则看不到如何通过模板来实现这种宏扩展。
如果您的目标是Objective-C
……请在Github上查看AWESOME KSVarArgs
KSVarArgs是一组宏,旨在使在Objective-C中更轻松地处理变量参数。所有宏都假定varargs列表仅包含Objective-C对象或类似对象的结构(可分配给ID类型)。基本宏ksva_iterate_list()遍历变量参数,为每个参数调用一个块,直到遇到终止nil。其他宏是为了方便在转换为通用集合时使用。
/*! @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
];