允许在 #ifdef 检查中使用自定义宏

3
想象一下,我有一些“断言式”功能,如果特定的宏被定义,则以某种方式声明宏,否则以另一种方式声明宏:
// in some header assert2.hpp

#ifdef NO_ASSERT2
#define assert2(x)
#else
#define assert2(x) assert2_handler(x);
#endif

在这里,NO_ASSERT2 宏与标准assert(3)中的 NDEBUG宏非常相似。
然而,我想做的是在包含文件之前允许用户使用自己的宏覆盖NO_ASSERT2检查。例如,如果你像这样包含assert2.hpp:
#define NO_ASSERT_KEY NO_ASSERT_CUSTOM
#include "assert2.hpp"

接着,对于这个翻译单元,将检查宏NO_ASSERT_CUSTOM而不是默认的NO_ASSERT2

它不一定要完全像上面那样工作 - 我只需要一种以每个文件为基础覆盖行为的方法,而不需要在包含位置处写入超过1行引导文本。


在第二种情况下,您定义了NO_ASSERT_KEY,您期望发生什么? - CoffeeTableEspresso
在这种情况下,如果通过编译命令行(例如 -DNO_ASSERT_CUSTOM)定义了 NO_ASSERT_CUSTOM,则 assert2 的定义将是虚拟的,就像在原始示例中定义了 NO_ASSERT2 一样。也就是说,NO_ASSERT_CUSTOM 取代了 NO_ASSERT2 的位置。 - BeeOnRope
当定义了NO_ASSERT_KEY时,我们将始终检查NO_ASSERT_CUSTOM,还是检查NO_ASSERT_KEY定义的任何值? - CoffeeTableEspresso
@CoffeeTableEspresso - 后者:当定义了 NO_ASSERT_KEY 时,您可以检查宏,其 nameNO_ASSERT_KEY 值。或者使用其他机制:它不必完全像这样工作(但必须有多个自定义键,即仅检查 NO_ASSERT_CUSTOM 不够)。 - BeeOnRope
天啊,这太难了。不幸的是,我不知道如何在预处理器中进行那种间接操作。 - CoffeeTableEspresso
1个回答

1
这并不美观...但这种方法可能适用于您。它假定该宏是使用#define FOO#define FOO 1-DFOO定义的(通常情况下,这将创建与#define FOO 1等效的东西)。
#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(A,B,...) B
#define GLUE3(A,B,C) GLUE3_I(A,B,C)
#define GLUE3_I(A,B,C) A##B##C
#define AGLUE3(A,B,C) AGLUE3_I(A,B,C)
#define AGLUE3_I(A,B,C) A##B##C
#define TEST_ASSERT_KEY GLUE3(NO_ASSERT_PROBE,0_,NO_ASSERT_KEY)
#define NO_ASSERT_PROBE0_NO_ASSERT_KEY AGLUE3(NO_ASSERT_PROBE,0_,NO_ASSERT2)
#define NO_ASSERT_PROBE0_  ,1
#define NO_ASSERT_PROBE0_1 ,1
#define NO_ASSERT_TEST SECOND(TEST_ASSERT_KEY,0)

With this, your usage would be:

#if NO_ASSERT_TEST 
#define assert2(x)
#else
#define assert2(x) assert2_handler(x);
#endif

这是一个 stacked-crooked 上的演示。
这里使用预处理器中的模式匹配,通过间接的 SECOND 宏来实现。其思想是它会扩展为其第二个参数,但只是间接地... 这允许你构建第一个参数作为一种模式。通常忽略第一个参数,但如果你想匹配某些东西,你可以设置它,使得第一个参数成为一个带有逗号扩展的宏; 这将移动一个新的第二个参数,替换默认值。
从这里开始,向后解释会更容易。 NO_ASSERT_TEST 使用TEST_ASSERT_KEY构建带有默认值0的模式。 TEST_ASSERT_KEY 构建NO_ASSERT_PROBE0_NO_ASSERT_KEY连接起来。 当定义了NO_ASSERT_KEY时,它将构建NO_ASSERT_PROBE0_与其定义的展开连接在一起。否则,它将使用NO_ASSERT_PROBE0_NO_ASSERT2重新构建测试标记。
无论哪种情况,这都是一个间接的粘贴,因此前一种情况下的NO_ASSERT_KEY或后一种情况下的NO_ASSERT2将首先被展开。在前一种情况下,如果说NO_ASSERT_KEYNO_ASSERT_CUSTOM并且NO_ASSERT_CUSTOM未定义,则会构建NO_ASSERT_PROBE0_NO_ASSERT_CUSTOM,这只是一个普通标识符,将被忽略,结果由于NO_ASSERT_TEST中的SECOND而导致0。但是,如果NO_ASSERT_CUSTOM按照#define NO_ASSERT_CUSTOM进行了定义,则会产生NO_ASSERT_PROBE0_,它会展开为,1,将1移入NO_ASSERT_TEST中的SECOND调用中。同样,如果NO_ASSERT_CUSTOM按照命令行上的-DNO_ASSERT_CUSTOM进行了定义,那么(通常)它的定义将等同于#define NO_ASSERT_CUSTOM 1,这将产生NO_ASSERT_PROBE0_1,它展开为,1
当未定义NO_ASSERT_KEY时的情况类似。
如果有人想尝试,可能有更漂亮的构建方式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接