【问题标题】:What does `va_num` mean in this variadic macro?这个可变参数宏中的“va_num”是什么意思?
【发布时间】:2017-06-26 06:39:15
【问题描述】:

似乎不同的编译器处理带有可变参数的宏略有不同。

对于 C99 兼容的编译器,使用特殊标识符 __VA_ARGS__ 来表示可变参数列表:

#define MACRO_1(param1, ...) func(param1, __VA_ARGS__)

对于 GNU C 编译器,可以为可变参数列表命名:

#define MACRO_2(param1, args...) func(param1, args) // "args..." is the name

但我在 ACPICA 代码中看到以下定义:

#define uefi_call_wrapper(func, va_num, ...) func(__VA_ARGS__)

对这样的宏的调用是这样的:

uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x0, 0, NULL);

这似乎是 C99 方法,但 va_num 似乎没用。为什么要定义?

ADD 1 - 宏的完整定义

#ifdef USE_EFI_FUNCTION_WRAPPER
#define __VA_NARG__(...)                        \
  __VA_NARG_(_0, ## __VA_ARGS__, __RSEQ_N())
#define __VA_NARG_(...)                         \
  __VA_ARG_N(__VA_ARGS__)
#define __VA_ARG_N(                             \
  _0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) N
#define __RSEQ_N()                              \
  10, 9,  8,  7,  6,  5,  4,  3,  2,  1,  0

#define __VA_ARG_NSUFFIX__(prefix,...)                  \
  __VA_ARG_NSUFFIX_N(prefix, __VA_NARG__(__VA_ARGS__))
#define __VA_ARG_NSUFFIX_N(prefix,nargs)        \
  __VA_ARG_NSUFFIX_N_(prefix, nargs)
#define __VA_ARG_NSUFFIX_N_(prefix,nargs)       \
  prefix ## nargs

/* Prototypes of EFI cdecl -> stdcall trampolines */

UINT64 efi_call0(void *func);
UINT64 efi_call1(void *func, UINT64 arg1);
UINT64 efi_call2(void *func, UINT64 arg1, UINT64 arg2);
UINT64 efi_call3(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3);
UINT64 efi_call4(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
                 UINT64 arg4);
UINT64 efi_call5(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
                 UINT64 arg4, UINT64 arg5);
UINT64 efi_call6(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
                 UINT64 arg4, UINT64 arg5, UINT64 arg6);
UINT64 efi_call7(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
                 UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7);
UINT64 efi_call8(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
                 UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
                 UINT64 arg8);
UINT64 efi_call9(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
                 UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
                 UINT64 arg8, UINT64 arg9);
UINT64 efi_call10(void *func, UINT64 arg1, UINT64 arg2, UINT64 arg3,
                  UINT64 arg4, UINT64 arg5, UINT64 arg6, UINT64 arg7,
                  UINT64 arg8, UINT64 arg9, UINT64 arg10);

/* Front-ends to efi_callX to avoid compiler warnings */

#define _cast64_efi_call0(f) \
  efi_call0(f)
#define _cast64_efi_call1(f,a1) \
  efi_call1(f, (UINT64)(a1))
#define _cast64_efi_call2(f,a1,a2) \
  efi_call2(f, (UINT64)(a1), (UINT64)(a2))
#define _cast64_efi_call3(f,a1,a2,a3) \
  efi_call3(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3))
#define _cast64_efi_call4(f,a1,a2,a3,a4) \
  efi_call4(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4))
#define _cast64_efi_call5(f,a1,a2,a3,a4,a5) \
  efi_call5(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
            (UINT64)(a5))
#define _cast64_efi_call6(f,a1,a2,a3,a4,a5,a6) \
  efi_call6(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
            (UINT64)(a5), (UINT64)(a6))
#define _cast64_efi_call7(f,a1,a2,a3,a4,a5,a6,a7) \
  efi_call7(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
            (UINT64)(a5), (UINT64)(a6), (UINT64)(a7))
#define _cast64_efi_call8(f,a1,a2,a3,a4,a5,a6,a7,a8) \
  efi_call8(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
            (UINT64)(a5), (UINT64)(a6), (UINT64)(a7), (UINT64)(a8))
#define _cast64_efi_call9(f,a1,a2,a3,a4,a5,a6,a7,a8,a9) \
  efi_call9(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
            (UINT64)(a5), (UINT64)(a6), (UINT64)(a7), (UINT64)(a8), \
            (UINT64)(a9))
#define _cast64_efi_call10(f,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) \
  efi_call10(f, (UINT64)(a1), (UINT64)(a2), (UINT64)(a3), (UINT64)(a4), \
             (UINT64)(a5), (UINT64)(a6), (UINT64)(a7), (UINT64)(a8), \
             (UINT64)(a9), (UINT64)(a10))

/* main wrapper (va_num ignored) */

#define uefi_call_wrapper(func,va_num,...)                        \
  __VA_ARG_NSUFFIX__(_cast64_efi_call, __VA_ARGS__) (func , ##__VA_ARGS__)

#else

#define uefi_call_wrapper(func, va_num, ...) func(__VA_ARGS__)

#endif

【问题讨论】:

  • 这似乎只是一个普通的宏参数,根本不用于宏扩展。
  • @Someprogrammerdude 但是为什么呢?有人告诉我这与 VC6 编译器有关。但我还是看不透。是否只是 VC6 预处理器的hint,以便 VC6 可以知道有多少个 args。但我认为这真的是不必要的,因为语法已经揭示了这一点。
  • 您可以创建一个带有 10 个参数的宏,但在宏扩展中不使用任何参数。这就是这里发生的情况,您的宏调用uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x0, 0, NULL) 导致扩展BS->SetWatchdogTimer(0, 0x0, 0, NULL)。就是这样。 va_num 参数没有被使用,它被丢弃了。仅仅因为它以 va_ 前缀命名并不意味着它必须是一个特殊的 C 符号。
  • 顺便问一下,uefi_call_wrapper 宏是在某些条件编译部分中定义的吗?是否存在uefi_call_wrapper 确实 使用va_num 参数的某些条件?您是否检查过源代码控制日志以查看它是否可能在过去的某个时间使用过?
  • @Someprogrammerdude 是的。让我发布完整的条件部分。

标签: c gcc visual-c++ c99


【解决方案1】:

va_num 只是传递给函数的普通参数,宏会忽略它。

用法一:

当宏在不同的编译器设置中使用时,这种用法很常见,以便在所有这些设置中调用相同的函数,但映射到不同的func,它可能(或可能不)支持va_num 参数。在这种特殊情况下,func 可能不支持va_num 参数,因此宏会忽略它。

用法2:

也可用于处理旧代码,使其易于移植到新版本的宏中。

回答您的问题,va_num 参数不是特殊参数。它与任何其他参数一样使用,与__VA_ARGS__ 或类似参数无关。宏的作者决定给那个参数起这个名字,只是为了说明它应该包含传递的参数的数量,但没有什么比这更花哨,也没有编译器评价。

【讨论】:

  • 是的,我刚刚向uefi_call_wrapper添加了一个通话示例。
  • 它也可能是宏的早期版本的剩余部分,出于兼容性原因而保留...
猜你喜欢
  • 2015-01-09
  • 1970-01-01
  • 2013-05-16
  • 1970-01-01
  • 2015-11-03
  • 1970-01-01
  • 2021-01-09
  • 2014-04-25
  • 2018-11-23
相关资源
最近更新 更多