c - 使用 __VA_ARGS__ 限制扩展参数的数量

标签 c c-preprocessor variadic-macros variadic-functions

我根据参数数量重载宏,如本问题所述: Overloading Macro on Number of Arguments

展开时,__VA_ARGS__“推送”宏名称,以便根据参数数量选择正确的宏名称,然后调用。

我的问题是我想要:

  • 使用 1 个参数时,重定向到 1 个参数重载
  • 使用 2 个参数时,重定向到 2 个参数重载
  • 当使用 > 2 个参数时,重定向到 2 个参数重载,额外的参数作为变量参数传递。

链接问题中的解决方案的问题是第三个参数被用作宏名称,这不起作用。

我希望有一种方法可以限制扩展__VA_ARGS__时扩展的参数数量。像这样的东西:

LIMIT_VA_ARGS_TO_2( ... ) ???

LIMIT_VA_ARGS_TO_2( 1 ) 给出:1

LIMIT_VA_ARGS_TO_2( 1, 2 ) 给出:1, 2

LIMIT_VA_ARGS_TO_2( 1, 2, 3 ) 还给出:1, 2

有办法做到这一点吗?

最佳答案

我没有找到实现此目的的简单方法,但通过一些辅助宏,我们可以做到这一点。

让我们构建一些助手。首先,如果您知道有两个或多个参数,那么很容易捕获第二个:

#define GET_ARG_2(A, B, ...) B

如果您不知道至少有两个参数,您可以随时添加一个参数,以确保还有第二个参数可供使用。我们可以随意称呼新的东西,但如果我们把它变成永远不会成为真正争论的东西,我们可以稍后识别它。这里我将其称为“EXPANDER(~)”,稍后我会解释原因。因此,即使我们只有一个参数,始终获取第二个参数的宏是:

#define FORCE_GET_ARG_2(...) GET_ARG_2( __VA_ARGS__, EXPANDER(~) )

好吧——下一组辅助宏都是用于间接的——我们推迟执行被调用的操作,以确保在执行列出的操作之前首先扩展作为参数一部分的任何宏。

#define MERGE(A, B) A ## B
#define INDIRECT_GET_ARG_2(...) GET_ARG_2( __VA_ARGS__)
#define INDIRECT_MERGE(A, B) MERGE(A,B)

现在是时候施展一点魔法了。我们将计算一个名为 COUNT_ARGS_CAP_2 的宏中有多少个参数,如果有一个参数,则返回 1,如果有两个或多个参数,则返回 2。为此,我们将扩展上面使用的神奇 EXPANDER(~) 占位符。

我们将对所有输入使用 FORCE_GET_ARG_2。如果存在真正的第二个参数,它将获取它并且将其算作一个输入参数。然后我们将数字 2 作为第二个参数(意味着最初至少有两个参数)。然而,如果我们发现 EXPANDER(~) 作为我们拉取的参数,我们希望它扩展以占据 2 个参数位置,第二个参数为 1(将 2 推开)。然后我们获取我们产生的第二个参数,它将是正确的答案。

#define DO_SPECIAL_EXPANDER(x) x, 1

#define COUNT_ARGS_CAP_2(...) \
  INDIRECT_GET_ARG_2( INDIRECT_MERGE(DO_SPECIAL_, FORCE_GET_ARG_2( __VA_ARGS__ )), 2 )

如果除 EXPANDER(~) 之外的任何参数都带有 DO_SPECIAL_ 前缀,它将毫无意义并被丢弃。但是如果 EXPANDER(~) 带有它的前缀,它将变成一个真正的宏并扩展到我们需要的两个参数!

所以,现在我们有一种方法来区分一个参数和两个或多个参数。让我们用它来构建您最初想要的宏!

#define LIMIT_VA_ARGS_TO_2( ... ) \
  INDIRECT_MERGE(LIMIT_VA_ARGS_V, COUNT_ARGS_CAP_2(__VA_ARGS__)) (__VA_ARGS__)
#define LIMIT_VA_ARGS_V1( A, ...) A
#define LIMIT_VA_ARGS_V2( A, B, ...) A, B

我们在这里所做的是根据原始参数的数量调用 LIMIT_VA_ARGS(V1 或 V2)的特殊子版本 - 我们确保它始终产生正确的答案。

可能有一种更简单的方法来完成所有这一切,但我无法轻易找到。 (我很想看看其他人是否这样做了!)

关于c - 使用 __VA_ARGS__ 限制扩展参数的数量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32434217/

相关文章:

c - 如果你的栈和堆是不可执行的,你的代码怎么跑?

c - 链表中的字符串问题。字段被最后一个节点的输入覆盖

c++ - 这个宏可以转换成一个函数吗?

c - 包含头文件时到底发生了什么?

c++ - 解压可变宏的任何技巧?

c - 为什么在使用 __VA_ARGS__ 从宏调用函数时 "vsprintf"会卡住?

使用 __VA_ARGS__ 连接字符串

python - 从 python 调用 c 程序时将数组传递给子进程模块的问题

c - tcl/Tk 'invalid command name ""' 通过管道控制时

c++ - 在代码中定义指令以分隔代码版本