Да, это возможно.
Чтобы включить выражение типа 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 )
использует constexpr, если (https://en.cppreference.com/w/cpp/language/if) импортировано с C ++ 17.
Чтобы обернуть все, у нас есть:
#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
И полный исходный код доступен в этом репо: https://github.com/fengwang/better_assert