简体   繁体   English

C 宏:如何将另一个宏映射到可变参数?

[英]C Macros: How to map another macro to variadic arguments?

I'd like to know how to apply a unary function (or another macro) to variadic arguments of a macro, like我想知道如何将一元函数(或另一个宏)应用于宏的可变参数,例如

int f(int a);

#define apply(args...) <the magic>
apply(a, b, c)

which unrolls展开

f(a)
f(b)
f(c)

Note that the number of arguments is unknown.请注意,参数的数量未知。

The code below is working for what you've asked for with up to 1024 arguments and without using additional stuff like boost.下面的代码适用于您所要求的最多 1024 个参数,并且没有使用像 boost 这样的额外东西。 It defines an EVAL(...) and also a MAP(m, first, ...) macro to do recursion and to use for each iteration the macro m with the next parameter first .它定义了一个EVAL(...)和一个MAP(m, first, ...)宏来进行递归并在每次迭代中使用宏m和下一个参数first

With the use of that, your apply(...) looks like: #define apply(...) EVAL(MAP(apply_, __VA_ARGS__)) .使用它,您的apply(...)看起来像: #define apply(...) EVAL(MAP(apply_, __VA_ARGS__))

It is mostly copied from C Pre-Processor Magic .它主要是从C Pre-Processor Magic复制而来的。 It is also great explained there.那里也有很好的解释。 You can also download these helper macros like EVAL(...) at this git repository , there are also a lot of explanation in the actual code.你也可以在这个git repository下载像EVAL(...)这样的辅助宏,实际代码中也有很多解释。 It is variadic so it takes the number of arguments you want.它是可变参数,所以它需要你想要的参数数量。

But I changed the FIRST and the SECOND macro as it uses a Gnu extension like it is in the source I've copied it from.但是我更改了FIRSTSECOND宏,因为它使用 Gnu 扩展,就像我从源中复制它一样。 This is said in the comments below by @ HWalters :这是@ HWalters在下面的评论中所说的

Specifically, 6.10.3p4: "Otherwise [the identifier-list ends in a ...] there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...)".具体来说,6.10.3p4:“否则 [标识符列表以 ... 结尾] 调用中的参数应多于宏定义中的参数(不包括 ...)”。

Main function part:主要功能部分:

int main()
{
   int a, b, c;
   apply(a, b, c) /* Expands to: f(a); f(b); f(c); */

   return 0;
}

Macro definitions:宏定义:

#define FIRST_(a, ...) a
#define SECOND_(a, b, ...) b

#define FIRST(...) FIRST_(__VA_ARGS__,)
#define SECOND(...) SECOND_(__VA_ARGS__,)

#define EMPTY()

#define EVAL(...) EVAL1024(__VA_ARGS__)
#define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__))
#define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__))
#define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__))
#define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__))
#define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__))
#define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__))
#define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__))
#define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__))
#define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__))
#define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__))
#define EVAL1(...) __VA_ARGS__

#define DEFER1(m) m EMPTY()
#define DEFER2(m) m EMPTY EMPTY()()

#define IS_PROBE(...) SECOND(__VA_ARGS__, 0)
#define PROBE() ~, 1

#define CAT(a,b) a ## b

#define NOT(x) IS_PROBE(CAT(_NOT_, x))
#define _NOT_0 PROBE()

#define BOOL(x) NOT(NOT(x))

#define IF_ELSE(condition) _IF_ELSE(BOOL(condition))
#define _IF_ELSE(condition) CAT(_IF_, condition)

#define _IF_1(...) __VA_ARGS__ _IF_1_ELSE
#define _IF_0(...)             _IF_0_ELSE

#define _IF_1_ELSE(...)
#define _IF_0_ELSE(...) __VA_ARGS__

#define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)())
#define _END_OF_ARGUMENTS_() 0

#define MAP(m, first, ...)           \
  m(first)                           \
  IF_ELSE(HAS_ARGS(__VA_ARGS__))(    \
    DEFER2(_MAP)()(m, __VA_ARGS__)   \
  )(                                 \
    /* Do nothing, just terminate */ \
  )
#define _MAP() MAP

#define apply_(x) f(x);
#define apply(...) EVAL(MAP(apply_, __VA_ARGS__))

To test macro expansion it is useful to use gcc with the command line argument -E :要测试宏扩展,将 gcc 与命令行参数-E一起使用很有用:

$ gcc -E srcFile.c

because your're getting concrete error messages and understand what's going on.因为您收到了具体的错误消息并了解发生了什么。

Everything is possible in C if you throw enough ugly macros at it.如果您向 C 中抛出足够多的丑陋宏,那么在 C 中一切皆有可能。 For example, you can have an ugly function-like macro:例如,你可以有一个丑陋的类似函数的宏:

#include <stdio.h>

int f (int a)
{
  printf("%d\n", a);
}

#define SIZEOF(arr) (sizeof(arr) / sizeof(*arr))

#define apply(...)                    \
{                                     \
  int arr[] = {__VA_ARGS__};          \
  for(size_t i=0; i<SIZEOF(arr); i++) \
  {                                   \
    f(arr[i]);                        \
  }                                   \
}

int main (void)
{
  apply(1, 2, 3);
}

Notice that 1) This would be much better off as a variadic function , and 2) it would be even better if you get rid of the variadic nonsense entirely and simply make a function such as请注意,1) 作为可变参数函数会好得多,并且 2) 如果您完全摆脱可变参数的废话并简单地创建一个函数,则效果会更好,例如

int f (size_t n, int array[n])

IMO impossible unless you use horrible tricks and hacks like this除非你使用像这样可怕的技巧和技巧,否则 IMO 是不可能的

https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s

And as I see it calls the same function so the type of the parameter is known - why you do not use stdarg instead.正如我所见,它调用了相同的函数,因此参数的类型是已知的——为什么不使用stdarg来代替。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM