在#define 内使用#ifdef 是不可能的。但仍有一些方法可以检测宏定义中是否定义了宏。
1。解决方案
godbolt
#define CAT(a, b) CAT_IMPL(a, b)
#define CAT_IMPL(a, b) a ## b
#define IS_DEBUG_DEFINED() CHECK((CAT(CHECK_,DEBUG), 0, 1))
#define CHECK_DEBUG ~,~
#define CHECK(tup) CHECK_IMPL tup
#define CHECK_IMPL(a, b, c, ...) c
#define IIF(condition, true_value, false_value) CAT(IIF_,condition)(true_value, false_value)
#define IIF_0(true_value, false_value) false_value
#define IIF_1(true_value, false_value) true_value
#define PRINT_IF_DEBUGGING(format) IIF(IS_DEBUG_DEFINED(), PRINT_DEBUGGING, PRINT_NOT_DEBUGGING)(format)
// this will be used if DEBUG is defined:
#define PRINT_DEBUGGING(format) debugPrint(format)
// this will be used if DEBUG is NOT defined:
#define PRINT_NOT_DEBUGGING(format) print(format)
PRINT_IF_DEBUGGING(foo) 将扩展为:
- 如果定义了
DEBUG:debugPrint(foo)
- 如果
DEBUG 未定义:print(foo)
例子:
PRINT_IF_DEBUGGING("test1");
#define DEBUG
PRINT_IF_DEBUGGING("test2");
#undef DEBUG
PRINT_IF_DEBUGGING("test3");
会导致:
print("test1");
debugPrint("test2");
print("test3");
2。 IS_DEBUG_DEFINED() 的工作原理
这背后的基本技巧是使用concatenation - 如果定义了宏,它将被扩展,否则预处理器不会修改令牌:
godbolt
#define CAT(a, b) CAT_IMPL(a, b)
#define CAT_IMPL(a, b) a ## b
// DEBUG NOT DEFINED:
CAT(CHECK_,DEBUG) // will expand to CHECK_DEBUG
// DEBUG DEFINED:
#define DEBUG 1234
CAT(CHECK_,DEBUG) // will expand to CHECK_1234
- 第一个
CAT 将扩展为 CHECK_DEBUG,因为未定义 DEBUG。
- 然而,第二个
CAT 将扩展为CHECK_1234,因为DEBUG 在与CHECK_ 连接之前已定义并扩展为1234。
通过定义一个名为 CHECK_DEBUG 的宏,如果未定义该宏,我们可以更改结果,例如:
godbolt
#define TEST CAT(CHECK_,DEBUG), 0, 1
#define CHECK_DEBUG ~,~
- 如果未定义
DEBUG,则结果将是 ~, ~, 0, 1(4 个逗号分隔的标记)
- 如果定义了
DEBUG,则结果将为CHECK_, 0, 1(3 个逗号分隔的标记)
请注意我们如何在第一种情况下获得 4 个令牌,但在第二种情况下只有 3 个令牌。
现在我们需要做的就是从该序列中获取第三个标记(如果未定义DEBUG,则为0,否则为1),例如使用始终返回第三个参数的简单宏:
#define CHECK(a, b, c, ...) c
综合起来,这就是完整的IS_DEBUG_DEFINED() 的样子:
godbolt
#define CAT(a, b) CAT_IMPL(a, b)
#define CAT_IMPL(a, b) a ## b
#define IS_DEBUG_DEFINED() CHECK((CAT(CHECK_,DEBUG), 0, 1))
#define CHECK_DEBUG ~,~
#define CHECK(tup) CHECK_IMPL tup
#define CHECK_IMPL(a, b, c, ...) c
如果DEBUG 未定义,IS_DEBUG_DEFINED() 将扩展为0,如果已定义1,例如:
IS_DEBUG_DEFINED() // -> 0
#define DEBUG
IS_DEBUG_DEFINED() // -> 1
#undef DEBUG
IS_DEBUG_DEFINED() // -> 0
使用IS_DEBUG_DEFINED(),您可以使用标准预处理器IIF 来更改宏的行为,具体取决于是否定义了DEBUG。
示例:godbolt
#define IIF(condition, true_value, false_value) CAT(IIF_,condition)(true_value, false_value)
#define IIF_0(true_value, false_value) false_value
#define IIF_1(true_value, false_value) true_value
#define PRINT_IF_DEBUGGING(format) IIF(IS_DEBUG_DEFINED(), PRINT_DEBUGGING, PRINT_NOT_DEBUGGING)(format)
// this will be used if DEBUG is defined:
#define PRINT_DEBUGGING(format) debugPrint(format)
// this will be used if DEBUG is NOT defined:
#define PRINT_NOT_DEBUGGING(format) print(format)
PRINT_IF_DEBUGGING("test"); // -> print("test");
#define DEBUG
PRINT_IF_DEBUGGING("test"); // -> debugPrint("test");
#undef DEBUG
PRINT_IF_DEBUGGING("test"); // -> print("test");
3。注意事项
对此的一个小警告是,如果定义了 DEBUG,它必须扩展为有效的预处理标记(因此它必须只包含字母、数字和下划线) - 否则连接将导致一个错误。
所以这不会工作:
#define DEBUG ()
#define DEBUG +