我不太确定你所说的“带逗号的布尔表达式”的含义。简单地在宏展开周围加上逗号(如下面的示例所示)可以防止在条件中使用默认逗号运算符时出现解析错误,但是默认的逗号运算符与 &&
的作用不同。如果你想要类似于这样的东西:
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
。
custom_assert((AClass<T1, T2>::aBoolMethod()), "aBoolMethod must be true");
。 - Adam Rosenfieldassert(expr && "some text");
,可以使用支持带逗号的表达式的custom_assert()
函数来实现,其实现非常简单,可以使用#define custom_assert(...) assert((__VA_ARGS__))
。这对你们来说是否足够呢?如果您确实需要将要检查的表达式与字符串分开,请将该字符串作为第一个参数,使用#define custom_assert(msg, ...) <...>
(其中再次使用__VA_ARGS__
表示表达式,可能包含逗号)会更容易实现。 - user743382