检测空参数然后使用默认大小写的 C 宏



我的宏要么会扩展成一个数字(在我的示例中,情况 2 和 4(,要么什么都没有(在我的示例中,情况 1 和 3(。虚无对我来说是不可取的,所以我需要其他宏来检测和替换虚无。

我能够创建一个宏,OLD_CHECK,它将检测虚无情况并将其替换为 0。(案例1(

我想改进它并创建一个新的宏,NEW_CHECK,它将检测虚无情况并将其替换为默认值。(案例3(

/* what I currently have */
print("case 1: [%d]", OLD_CHECK( ));                   // case 1: [0]
print("case 2: [%d]", OLD_CHECK(5));                   // case 2: [5]
/* what I hope to achieve */
int default_value = 7;
print("case 3: [%d]", NEW_CHECK(default_value,  ));    // case 3: [7]
print("case 4: [%d]", NEW_CHECK(default_value, 5));    // case 4: [5]

为了写OLD_CHECK,我使用了参数的数量。使用此方法编写NEW_CHECK似乎不可行,因为情况 3 和 4 都被检测为具有 2 个参数。

编辑:这是用于捕获和处理情况 1 和 2 的宏:

/* Given a dummy and >=1 arguments, expand to the first argument */
#define FIRST_ONE(dummy, a1, ...) a1
/* if "..." is nonblank, expand to the first arg of "...". otherwise, expand to 0 */
#define OLD_CHECK(...) OLD_CHECK_CORE(__VA_ARGS__) 
#define OLD_CHECK_CORE(...) FIRST_ONE(dummy, ##__VA_ARGS__, 0)

编辑2:在案例3中必须有尾随逗号。它是一系列其他更复杂的宏的不幸副产品。

回答: p00ya给出的这种方法对我有用:

#define NEW_CHECK_HELPER(...) , ## __VA_ARGS__
#define NEW_CHECK(default, ...) (default NEW_CHECK_HELPER(__VA_ARGS__))

使用 GCC 的, ## __VA_ARGS__扩展名进行逗号删除(您已经在OLD_CHECK中使用(:

#include <stdio.h>
#define NEW_CHECK_HELPER(...) , ## __VA_ARGS__
#define NEW_CHECK(default, ...) (default NEW_CHECK_HELPER(__VA_ARGS__))
int
main() {
int default_value = 7;
printf("case 3: %dn", NEW_CHECK(default_value, ));
printf("case 4: %dn", NEW_CHECK(default_value, 5));
}

指纹:

case 3: 7
case 4: 5

如果你真的需要支持案例 3 中的"空参数",那么你可以尝试非可移植的:

#include <stdio.h>
#define NEW_CHECK(default, ...) (default __VA_OPT__(, __VA_ARGS__))
int
main() {
int default_value = 7;
printf("case 3: %dn", NEW_CHECK(default_value, ));
printf("case 4: %dn", NEW_CHECK(default_value, 5));
}

当使用最新版本的 gcc 编译时(例如 10.1,请参阅 https://godbolt.org/z/AhST8m(,这将打印:

case 3: 7
case 4: 5

它依赖于非标准__VA_OPT__,这是为C提出的,但作为GCC扩展得到支持。

不过,我会质疑您在案例 3 中包含尾随逗号的要求。

如果您删除了情况三中的尾随逗号,则现有方法将起作用:

#define FIRST_ONE(dummy, a1, ...) a1
#define NEW_CHECK(DEFAULT, ...) FIRST_ONE(dummy,##__VA_ARGS__, DEFAULT)
/* what I hope to achieve */
int default_value = 7;
print("case 3: [%d]", NEW_CHECK(default_value  ));    // case 3: [7]
print("case 4: [%d]", NEW_CHECK(default_value, 5));    // case 4: [5]

原因是GCC区分了无参数和空参数,##__VA_ARGS__- 只有当没有参数时,它才会删除逗号 - (单个(空参数保留它。

然后有一种奇怪的是,OLD_CHECK( )被视为没有论据而不是空洞的论点,这可以说没有多大意义。

一段时间后,我想出了一个替代的多步骤解决方案,它与OLD_CHECK一样便携。它重用以前的FIRST_ONE,还使用标准NARGS宏来计算__VA_ARGS__中的参数数。

/* NEW_CHECK(default, maybe_blank) */
#define NEW_CHECK(...) M_(__VA_ARGS__)
/* delay expansion of M once to perform Q */
#define M_(D, ...) F(D, Q(__VA_ARGS__))
/* Q(maybe_blank): if __VA_ARGS__ is non-blank, __VA_ARGS__. Otherwise, SPACE */
#define Q(...) FIRST_ONE(dummy, ##__VA_ARGS__, SPACE)
/* SPACE just expands into two arguments so that we can differentiate between was-blank (2 args) and non-blank (1 arg) */
#define SPACE 0, 0
/* F(default, one-or-two-arguments) */
#define F(...) F_(__VA_ARGS__)
#define F_(...) F__(__VA_ARGS__)
/* delay expansion of F twice, for performing Q and SPACE */
#define F__(D, ...) H(NARGS(__VA_ARGS__), D, __VA_ARGS__)
/* H(1 or 2, default, maybe_blank) */
#define H(...) H_(__VA_ARGS__)
/* delay expansion once, for performing NARGS */
#define H_(N, D, ...) DO##N(D, __VA_ARGS__)
/* based on NARGS, pick correct result */
#define DO1(D, ...) __VA_ARGS__
#define DO2(D, ...) D

然而,这个丑陋的怪物是没有必要的,因为这个答案对我也很有效,而且干净得多。非常感谢大家!

最新更新