【问题标题】:Limit number of expanded arguments with __VA_ARGS__使用 __VA_ARGS__ 限制扩展参数的数量
【发布时间】:2015-12-02 17:55:15
【问题描述】:

我正在根据参数数量重载宏,如本问题所述: 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

有没有办法做到这一点?

【问题讨论】:

  • 是的,使用 Order 宏库。 Here 是关于 SO 的先前答案,演示了如何将其用于不同的示例。
  • 谢谢。但是你知道我如何在不使用库的情况下做到这一点吗?
  • 你不能使用this answer,而是用2, 2, 2, ... 2, 2, 1, 0填充长度计数表(__RSEQ_N)吗?

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


【解决方案1】:

我没有看到一个简单的方法来完成这个,但是通过一些辅助宏,我们可以做到。

让我们构建一些这样的助手。首先,如果你知道你有两个或多个参数,那么很容易抓住第二个:

#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)——我们确保它总是产生正确的答案。

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

【讨论】:

  • 感谢您抽出宝贵时间做出如此详细的回答。稍后我会看看这个。
猜你喜欢
  • 2015-11-30
  • 2012-08-01
  • 2021-05-23
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 2011-01-08
  • 2011-07-05
相关资源
最近更新 更多