我没有看到一个简单的方法来完成这个,但是通过一些辅助宏,我们可以做到。
让我们构建一些这样的助手。首先,如果你知道你有两个或多个参数,那么很容易抓住第二个:
#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)——我们确保它总是产生正确的答案。
可能有一种更简单的方法可以完成所有这些,但我无法轻易找到。 (不过我很想看看其他人是否这样做!)