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

时间:2019-06-25 21:41:21

标签: c++ c-preprocessor

想象一下,我有一些“声明类似”功能,如果定义了特定的宏,则以一种方式声明宏,否则,则以另一种方式声明宏:

// 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行样板。 / p>

1 个答案:

答案 0 :(得分: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)

这样,您的用法将是:

#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,它只是一个普通标识符,将被忽略,从而导致0中的SECOND导致NO_ASSERT_TEST中的数据丢失。但是,如果为每个NO_ASSERT_CUSTOM定义了#define NO_ASSERT_CUSTOM,则会产生NO_ASSERT_PROBE0_,并扩展为,1,从而将1移入{ {1}}。同样,如果在命令行上按SECOND定义了NO_ASSERT_TEST,则(通常)使其定义等同于NO_ASSERT_CUSTOM,这将产生-DNO_ASSERT_CUSTOM,并扩展为{{ 1}}。

未定义#define NO_ASSERT_CUSTOM 1的情况类似。

如果有人想拍摄,可能有一种更漂亮的方法来构建此构造。