是否可以遍历可变参数宏中的参数?

问题描述 投票:66回答:9

我想知道是否有可能对C99中传递给可变参数宏的参数或使用任何GCC扩展进行迭代?

例如是否可以编写一个通用宏,该宏采用一个结构及其字段作为参数传递并打印该结构中每个字段的偏移量?

类似这样的东西:

构造一个{诠释int b;int c;};/ * PRN_STRUCT_OFFSETS将打印每个字段的偏移量内部结构作为第一个参数传递。* /int main(int argc,char * argv []){PRN_STRUCT_OFFSETS(结构a,a,b,c);返回0;}
c foreach c99 c-preprocessor variadic
9个回答
65
投票

[这是我今天的作业,它基于宏技巧,今天我特别了解了__VA_NARG__ invented by Laurent Deniau。无论如何,为了清晰起见,以下示例代码最多可以处理8个字段。如果需要更多,只需通过复制来扩展代码(这是因为预处理器不具有递归功能,因为它仅读取文件一次)。

__VA_NARG__

打印输出:

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

42
投票

[冒着获得考古学家徽章的风险,我认为使用#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:

Overloading Macro on Number of Arguments

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

15
投票

如果您的结构用Outer::Next::Inner::MyFoo foo(); namespace Outer {namespace Next {namespace Inner { class Foo; }}} 描述,则可以编写函数或宏以遍历结构的所有字段并打印其偏移量。

X-Macros

6
投票

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

  1. 使用pedantic选项进行编译,我得到了警告:“ ISO99要求使用rest参数。”这是由第一个FOR_EACH_1宏中的variad参数引起的。删除这些警告并在FOR_EACH_2中将呼叫更改为FOR_EACH_1会删除此警告。

    #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;
    }
    
  2. 因为我以一种非常通用的方式使用它,所以有时我不得不只用1个参数调用repeat宏。 (我知道重复1次是没有意义的;))。幸运的是,解决此问题的方法非常简单。只需从FOR_EACH宏中删除x参数。

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

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

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

5
投票

也许将varargs用作数组初始化程序,并遍历countof(array)?即sizeof(array)/ sizeof(array [0])。该数组可能是C99匿名数组。

我想不出另一种方法来遍历宏的var-args,因为我不知道如何对每个var-arg元素的文本执行任何操作。 var-arg部分也可能是其中包含逗号的单个参数,因为您可以使用CPP,AFAIK对它进行全部处理。

但是这是我遍历var-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__)

0
投票

这是我能想到的最好的标准C:

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

0
投票

这是我为此的解决方案享受

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

0
投票

如果您定位为#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; } …请查看AWESOME Objective-C

KSVarArgs是一组宏,旨在使在Objective-C中更轻松地处理变量参数。所有宏都假定varargs列表仅包含Objective-C对象或类似对象的结构(可分配给ID类型)。基本宏ksva_iterate_list()遍历变量参数,为每个参数调用一个块,直到遇到终止nil。其他宏是为了方便在转换为通用集合时使用。

KSVarArgs on Github

示例用法:

/*! @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);
     }];    
  }];
}

-1
投票

我将其添加为另一个答案。这是使用C ++ 0x(使用g ++ 4.5.0编译)进行的尝试。

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

程序打印

123

但是,使用这种方法,传递给lambda表达式的所有参数都必须具有相同的类型,在上面的示例中为#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; } 。但是,lambda允许您捕获变量,例如:

int

打印输出:

int main()
{
  int offset = 10;

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

  return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.