c++ - 如何使用可选的格式化消息实现符合标准的断言宏?

标签 c++ macros assert

使用可选格式化消息实现符合标准的断言宏的方法是什么?

我在 clang 中的工作,但(正确地)触发 -Wgnu-zero-variadic-macro-arguments 警告如果它被打开(例如通过 -Wpedantic) 当在没有可选消息的情况下使用宏时。 Wandbox

#define MyAssert(expression, ...)                                      \
    do {                                                               \
        if(!(expression))                                              \
        {                                                              \
            printf("Assertion error: " #expression " | " __VA_ARGS__); \
            abort();                                                   \
        }                                                              \
    } while(0)

最佳答案

一个人需要真正最大限度地使用预处理器,以便区分没有额外参数和它们存在的情况。但是使用 Boost.PP 可以做到这一点:

#include <boost/preprocessor/variadic/size.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/logical/bool.hpp>
#include <boost/preprocessor/cat.hpp>


#define MyAssert(...) BOOST_PP_CAT(MY_ASSERT,BOOST_PP_BOOL(BOOST_PP_SUB(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1)))(__VA_ARGS__)

#define MY_ASSERT0(expr) MY_ASSERT1(expr,)

#define MY_ASSERT1(expression, ...)                                    \
    do {                                                               \
        if(!(expression))                                              \
        {                                                              \
            std::printf("Assertion error: " #expression " | " __VA_ARGS__); \
            std::abort();                                              \
        }                                                              \
    } while(0)

MyAssert 必须接受至少一个参数(标准)。然后我们计算参数,减去一个,然后转为 bool 值(0 或 1)。这个 0 或 1 连接到 token MY_ASSERT 以形成一个宏名称,我们继续将参数转发给它。

MY_ASSERT1(带参数)是您的原始宏。 MY_ASSERT0 将自身替换为 MY_ASSERT1(expr,),尾随的逗号表示我们传递了另一个参数(因此满足了对一个额外参数的要求),但它是一个空标记顺序,所以它什么都不做。

您可以 see it live .


因为我们已经进入了这个兔子洞,如果不想引入 Boost.PP,可以使用通常的参数计数技巧来完成上述操作,稍作修改。首先,我们必须决定我们允许的参数的最大限制。我选择了20个,你可以选择更多。我们需要典型的 CONCAT 宏,这里是这个宏:

#define HAS_ARGS(...) HAS_ARGS_(__VA_ARGS__,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,)
#define HAS_ARGS_(a1,a2,a3,a4,a5,b1,b2,b3,b4,b5,c1,c2,c3,c4,c5,d1,d2,d3,d4,d5,e, N, ...) N

这是计算参数,但有一个转折点。当 __VA_ARGS__ 是单个参数(没有额外的参数)时,N 解析为 0。否则,它被解析为 1。最多可以有 20 个额外参数表达式,其中任何数量都将解析为相同的 1。现在我们只需将它插入我们之前使用 boost 的相同位置:

#define MyAssert(...) CONCAT(MY_ASSERT, HAS_ARGS(__VA_ARGS__))(__VA_ARGS__)

You can tinker with it here

关于c++ - 如何使用可选的格式化消息实现符合标准的断言宏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53986620/

相关文章:

c++ - 将 Keras 模型转换为 C++

c++ - g++:使用 ZIP 文件作为输入

C++宏问题(逗号,的解释)

abap - ABAP 中的断言

c++ - Windows 编程。用空括号创建对象

c++ - C++识别文件类型

动态使用不同定义的 C 宏

macros - 在 Clojure 中将宏分解为宏或函数之间有什么区别吗?

C++ CppUnit 测试 (CPPUNIT_ASSERT)

Java Assert Double 是 NaN