自定义`assert`宏,支持逗号和错误消息

时间:2014-02-22 17:08:52

标签: c++ c++11 macros assert

我想创建assert中定义的<cassert>宏的自定义版本,当断言失败时会显示错误消息。


所需用法:

custom_assert(AClass<T1, T2>::aBoolMethod(), "aBoolMethod must be true");


有缺陷的测试实施:

#define custom_assert(mCondition, mMessage) ...
// This fails because mCondition may have commas in it

#define custom_assert(..., mMessage)
// Not sure about this either - mMessage may be an expression containing commas
// as well

如何正确实现一个自定义断言,它将一个布尔表达式(带有可能的逗号)作为第一个参数,一个字符串表达式(带有可能的逗号)作为第二个参数?

或者有没有办法在不使用宏的情况下实现断言?

4 个答案:

答案 0 :(得分:3)

你非常接近,你需要使用的只是这个:

#define myAssert(message, ...) do { \
    if(!(__VA_ARGS__)) { \
        /*error code*/ \
    } \
} while(0)

特殊预处理器变量__VA_ARGS__将扩展为在三个点的位置传递的任何内容,包括所有逗号。

请注意,预处理器至少不会在条件中解释逗号,它只是将它们按原样粘贴到if()语句中。正如评论所暗示的那样,如果你想传递模板条件,那么这正是你想要的。

消息字符串中的逗号也不是问题,因为预处理器了解字符串文字并且不解释双引号内的任何内容。

答案 1 :(得分:1)

直截了当的

assert(AClass<T1, T2>::aBoolMethod() && "aBoolMethod must be true");

失败:

  

错误:宏“断言”传递了2个参数,但只占用1个

但如果你在第一个参数周围添加一对额外的括号,它就可以了。像这样:

#include <cassert>

template <typename A, typename B>
struct C { 
  bool f() { return false; }
};

int main() {
  assert((C<int,int>().f()) && "some message, with comma");
  //     ^                ^
}

注意:Adam Rosenfield在评论中也指出了它。

__VA_ARGS__ approach相比,唯一的好处就是它不会在用户上转储另一个宏。如果您忘记了括号,则可能会出现编译时错误,因此我将其视为安全解决方案。

答案 2 :(得分:1)

为了完整起见,我在C ++中发布了一个drop-in 2文件断言宏实现:

#include <pempek_assert.h>

int main()
{
  float min = 0.0f;
  float max = 1.0f;
  float v = 2.0f;
  PEMPEK_ASSERT(v > min && v < max,
                "invalid value: %f, must be between %f and %f", v, min, max);

  return 0;
}

将提示您:

Assertion 'v > min && v < max' failed (DEBUG)
  in file e.cpp, line 8
  function: int main()
  with message: invalid value: 2.000000, must be between 0.000000 and 1.000000

Press (I)gnore / Ignore (F)orever / Ignore (A)ll / (D)ebug / A(b)ort:

哪里

  • (I)gnore:忽略当前的断言
  • 忽略(F)orever:记住断言触发的文件和行 忽略它以继续执行程序
  • 忽略(A)ll:忽略所有剩余的断言(所有文件和行)
  • (D)ebug:如果连接则进入调试器,否则abort()(在Windows上, 系统将提示用户附加调试器)
  • A(b)ort:立即致电abort()

你可以在那里找到更多相关信息:

希望有所帮助。

答案 3 :(得分:0)

我不完全确定“带逗号的布尔表达式”是什么意思。简单地用逗号包装宏扩展(如下面的示例所示)可以防止在您的条件中使用默认逗号运算符时出现解析错误,但默认的逗号运算符与&&不同。如果你的意思是你想要的东西:

my_assert(condition1, condition2, message1, message2);

你运气不好。任何API应如何告知条件停止的位置以及消息的开始。只需使用&&即可。您可以使用下面相同的技巧来处理消息部分,也可以创建一个my_condition_set宏,允许您编写如下内容:

my_asssert(my_condition_set(condition1, condition2), message1, message2);

进入消息部分,这是标准assert宏往往缺乏的棘手部分和最有用的部分,诀窍将归结为使用覆盖{{1的自定义消息输出类型(逗号运算符)或使用可变参数模板。

宏使用variadic宏支持和一些技巧来处理逗号。请注意,我在这里发布的代码都没有直接测试;它来自我过去写过的自定义断言宏的内存。

使用逗号运算符重载的版本,适用于C ++ 98编译器:

operator,

struct logger { template <typename T> logger& operator,(const T& value) { std::cerr << value; return *this; } }; #define my_assert(condition, ...) do{ \ if (!(condition)) { \ (logger() , __VA_ARGS__); \ std::terminate(); \ } \ }while(false) 表达式创建logger()类型的新实例。然后粘贴logger的参数列表,并用逗号分隔每个参数。这些逗号分别从左到右调用逗号运算符,将表达式转发到__VA_ARGS__。我通常使用自定义日志流来处理文件写入,std::cerr,Windows“cerr或其他任何内容。

在C ++ 11编译器中使用可变参数模板,这更像是:

OutputDebugStringA

您需要一个格式字符串函数,它实际上可以使用可变参数或为template <typename ...Ts> void logger(Ts&&... argv) { std::cerr << your_string_format_function(argv...); } void logger(const char* fmt) { std::cerr << fmt; } void logger() {} #define my_assert(condition, ...) do{ \ if (!(condition)) { \ logger(__VA_ARGS__); \ std::terminate(); \ } \ }while(false) 之类的函数编写适配器。

如果您不介意缺乏类型安全性,也可以使用boost::format