我的宏要么会扩展成一个数字(在我的示例中,情况 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
然而,这个丑陋的怪物是没有必要的,因为这个答案对我也很有效,而且干净得多。非常感谢大家!