是的,这是可能的。
要启用像better_assert((0 < x) && (x < 10), std::string("x was ") + myToString(x)); 这样的表达式,我们应该有一个相应的宏
#define better_assert(EXPRESSION, ... ) ((EXPRESSION) ? \
(void)0 : print_assertion(std::cerr, \
"Assertion failure: ", #EXPRESSION, " in File: ", __FILE__, \
" in Line: ", __LINE__ __VA_OPT__(,) __VA_ARGS__))
其中print_assertion 是执行断言的代理函数。当EXPRESSION 被评估false 时,所有调试信息__VA_ARGS__ 将被转储到std::cerr。这个函数接受任意数量的参数,因此我们应该实现一个可变参数模板函数:
template< typename... Args >
void print_assertion(std::ostream& out, Args&&... args)
{
out.precision( 20 );
if constexpr( debug_mode )
{
(out << ... << args) << std::endl;
abort();
}
}
在前面的实现中,表达式(out << ... << args) << std::endl;利用了C++17中的折叠表达式(https://en.cppreference.com/w/cpp/language/fold);常量表达式debug_mode与传递的编译选项有关,可以定义为
#ifdef NDEBUG
constexpr std::uint_least64_t debug_mode = 0;
#else
constexpr std::uint_least64_t debug_mode = 1;
#endif
还值得一提的是,表达式if constexpr( debug_mode ) 使用了自 C++17 以来导入的 constexpr if (https://en.cppreference.com/w/cpp/language/if)。
总结一下,我们有:
#ifdef NDEBUG
constexpr std::uint_least64_t debug_mode = 0;
#else
constexpr std::uint_least64_t debug_mode = 1;
#endif
template< typename... Args >
void print_assertion(std::ostream& out, Args&&... args)
{
out.precision( 20 );
if constexpr( debug_mode )
{
(out << ... << args) << std::endl;
abort();
}
}
#ifdef better_assert
#undef better_assert
#endif
#define better_assert(EXPRESSION, ... ) ((EXPRESSION) ? (void)0 : print_assertion(std::cerr, "Assertion failure: ", #EXPRESSION, " in File: ", __FILE__, " in Line: ", __LINE__ __VA_OPT__(,) __VA_ARGS__))
展示其用法的典型测试用例可以是:
double const a = 3.14159265358979;
double const b = 2.0 * std::asin( 1.0 );
better_assert( a==b, " a is supposed to be equal to b, but now a = ", a, " and b = ", b );
这会产生类似的错误信息:
Assertion failure: a==b in File: test.cc in Line: 9 a is supposed to be equal to b, but now a = 3.1415926535897900074 and b = 3.141592653589793116
[1] 8414 abort (core dumped) ./test
完整的源代码可以在这个 repo 中找到:https://github.com/fengwang/better_assert