我想创建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
如何正确实现一个自定义断言,它将一个布尔表达式(带有可能的逗号)作为第一个参数,一个字符串表达式(带有可能的逗号)作为第二个参数?
或者有没有办法在不使用宏的情况下实现断言?
答案 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:
哪里
abort()
(在Windows上,
系统将提示用户附加调试器)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
。