使用__VA_ARGS __

时间:2015-09-07 08:29:48

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

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

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

我的问题是我想要:

  • 使用1参数时,重定向到1参数重载
  • 使用2个参数时,重定向到2个参数overload
  • 使用> 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

有办法吗?

1 个答案:

答案 0 :(得分:3)

我没有看到一种简单的方法来实现这一点,但是有了一些辅助宏,我们就可以做到。

让我们建立一些帮手。首先,如果你知道你有两个或更多的论点,那么很容易抓住第二个:

#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)的特殊子版本 - 我们确保它始终产生正确的答案。

可能有一种更简单的方法来完成所有这些,但我无法轻易找到一个。 (我很想看看别人是否做过!)