自定义"assert"宏,支持逗号和错误消息
Custom `assert` macro that supports commas and error message
我想创建<cassert>
中定义的assert
宏的自定义版本,该版本在断言失败时显示错误消息。
所需用途:
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
如何正确实现将布尔表达式(可能带有逗号)作为第一个参数,将字符串表达式(可能带逗号)作为第二个参数的自定义断言?
或者有没有一种方法可以在不使用宏的情况下实现断言?
您非常接近,您需要使用的只是:
#define myAssert(message, ...) do {
if(!(__VA_ARGS__)) {
/*error code*/
}
} while(0)
特殊的预处理器变量__VA_ARGS__
将扩展到在三个点的位置传递的任何内容,包括所有coma。
请注意,预处理器根本不会解释条件中的逗号,它只会将它们原样粘贴到if()
语句中。如果您想要传递模板化条件,正如注释所暗示的那样,这正是您想要的。
消息字符串中的逗号也不是问题,因为预处理器了解字符串文字,并且不解释双引号中的任何内容。
直接的
assert(AClass<T1, T2>::aBoolMethod() && "aBoolMethod must be true");
失败:
错误:宏"assert"传递了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");
// ^ ^
}
注:亚当·罗森菲尔德在评论中也指出了这一点。
与__VA_ARGS__
方法相比,可能唯一的好处是它不会向用户转储另一个宏。如果你忘记了括号,你可能会得到一个编译时错误,所以我认为这是一个安全的解决方案。
为了完整起见,我发布了一个在C++中的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:忽略当前断言
- Ignore(F)orever:记住触发断言的文件和行在程序的剩余执行中忽略它
- Ignore(A)ll:忽略所有剩余断言(所有文件和行)
- (D) ebug:如果附加,则闯入调试器,否则为
abort()
(在Windows上,系统将提示用户附加调试器) - A(b)端口:立即呼叫
abort()
你可以在那里找到更多关于它的信息:
- 博客文章
- GitHub项目
希望能有所帮助。
我不完全确定你所说的"带逗号的布尔表达式"是什么意思。如果你碰巧在条件中使用默认逗号运算符,那么简单地用逗号包装宏展开(如下面的示例所示)可以防止解析错误,但默认逗号运算符的作用与&&
不同。如果你的意思是你想要这样的东西:
my_assert(condition1, condition2, message1, message2);
你运气不好。任何API都应该如何判断条件停止和消息开始的位置。只需使用&&
即可。您可以使用以下相同的技巧来处理消息部分,还可以创建一个my_condition_set
宏,该宏允许您编写以下内容:
my_asssert(my_condition_set(condition1, condition2), message1, message2);
进入消息部分,这是标准assert
宏往往缺乏的棘手部分和最有用的部分,技巧将归结为要么使用覆盖operator,
(逗号运算符)的自定义消息输出类型,要么使用可变模板。
该宏使用可变的宏支持和一些技巧来处理逗号。请注意,我在这里发布的代码没有一个是直接测试的;这一切都来自我过去写的自定义断言宏的内存。
使用逗号运算符重载的版本,适用于C++98编译器:
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
。我通常使用一个自定义日志流来处理对文件、cerr
、Windows的OutputDebugStringA
或其他文件的写入。
在C++11编译器中使用可变模板,这将更像:
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
之类的东西编写一个适配器。
如果你不介意缺乏类型安全性,你也可以使用printf
。
- boost::进程间消息队列引发错误
- 在线编译器中的分段C++没有打印消息
- C++错误消息*成员参考.**初学者*
- 在createdialog创建的窗口中捕获用于编辑控件的OnMouseMove消息
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 如何通过参数抛出错误消息
- 从服务器传输到客户端的消息不会出现
- ROS2 动态消息模板
- C++秘密消息学校作业
- glad 导致 glfwSwapBuffers 返回错误消息
- C++入门 5 版:类消息和文件夹
- FindPackageHandleStandardArgs.cmake:137 的 CMake 错误(消息):找不到 Boost (缺少:正则表达式)(找到合适的版本"1.72.0",
- 如何处理从一个对象传递到另一个在C++中具有公共抽象类的对象的消息
- 如何接受 [ENTER] 键作为无效输入并发送错误消息
- 由于无效的 ValidateRgn() 子窗口不会收到WM_PAINT消息
- "string.h"在构建适用于iOS的qt应用程序中找不到消息
- 如何将 Firebase 与基于 Linux 的客户端应用配合使用,以便与服务器进行双向消息通信
- 重新定义预定义的 errno 错误消息 (E2BIG)
- Libmosquitto publish 不会将所有消息传递到 Azure IoT Hub
- 错误消息:"jj"的名称查找已更改为ISO"for"范围,(如果您使用"