Добавить пользовательские сообщения в assert?


130

Есть ли способ добавить или отредактировать сообщение, созданное assert? Я бы хотел использовать что-то вроде

assert(a == b, "A must be equal to B");

Затем компилятор добавляет строку , время и так далее ...

Является ли это возможным?


Вы можете определить макрос вот так .
HelloGoodbye

Ответы:


241

Хакер, который я видел, - это использование &&оператора. Поскольку указатель "истинен", если он не равен нулю, вы можете сделать следующее, не изменяя условия:

assert(a == b && "A is not equal to B");

Поскольку assertпоказывает условие, которое не удалось, оно также отобразит ваше сообщение. Если этого недостаточно, вы можете написать свою собственную myAssertфункцию или макрос, который будет отображать все, что вы хотите.


27
Другой вариант - перевернуть операнды и использовать оператор запятой. Вам нужны дополнительные круглые скобки, чтобы запятая не рассматривалась как разделитель между аргументами:assert(("A must be equal to B", a == b));
Кейт Томпсон,

3
Однако было бы неплохо иметь возможность печатать значения переменных, например:assert(a == b && "A (" << A << ") is not equal to B (" << B << ")");
Фрэнк

7
@Frank printfвозвращает ненулевое значение, если он что-то напечатал, поэтому вы могли бы сделать что-то подобное assert(a == b && printf("a (%i) is not equal to b (%i)", a, b)), хотя в этот момент вам, вероятно, следует написать свою собственную оболочку assert.
zneak 04

1
Плохой код! Я этого не понимаю! Если a == b ложно, выражение and также должно быть ложным, и, следовательно, строка не должна оцениваться.
ragnarius 07

1
@TUIlover, строковые литералы C работают не так; они являются константами времени компиляции, и их использование в этом контексте тривиально оптимизируется. Нет затрат на время выполнения.
zneak 09

46

Другой вариант - поменять местами операнды и использовать оператор запятой. Вам нужны дополнительные круглые скобки, чтобы запятая не рассматривалась как разделитель между аргументами:

assert(("A must be equal to B", a == b));

(это было скопировано из приведенных выше комментариев для лучшей видимости)


3
Это отличный подход, с одной крошечной проблемой, он будет отображать «предупреждение: левый операнд оператора запятой не действует» при компиляции в g ++ с помощью `-Wunused-value
v010dya

1
или с помощью макроса: #ifndef m_assert #define m_assert (expr, msg) assert ((msg, expr)) #endif
Szymon Marczak 03

Использование оболочки макроса позволяет избежать предупреждения gcc:#define m_assert(expr, msg) assert(( (void)(msg), (expr) ))
Jander

25

Вот моя версия макроса assert, который принимает сообщение и распечатывает все в понятной форме:

#include <iostream>

#ifndef NDEBUG
#   define M_Assert(Expr, Msg) \
    __M_Assert(#Expr, Expr, __FILE__, __LINE__, Msg)
#else
#   define M_Assert(Expr, Msg) ;
#endif

void __M_Assert(const char* expr_str, bool expr, const char* file, int line, const char* msg)
{
    if (!expr)
    {
        std::cerr << "Assert failed:\t" << msg << "\n"
            << "Expected:\t" << expr_str << "\n"
            << "Source:\t\t" << file << ", line " << line << "\n";
        abort();
    }
}

Теперь вы можете использовать это

M_Assert(ptr != nullptr, "MyFunction: requires non-null argument");

А в случае сбоя вы получите такое сообщение:

Ошибка утверждения: MyFunction: требуется ненулевой аргумент

Ожидается: ptr! = Nullptr

Источник: C: \ MyProject \ src.cpp, строка 22

Красиво и чисто, смело используйте его в своем коде =)


Хороший. Очень полезно
Killrazor

Я немного запутался. Считается ли #Expr строкой для прямой подстановки? В чем разница между #Expr и Expr?
Минь Тран,

@MinhTran Предположим, что ваше условие утверждения - x == y. Затем Expr будет расширяться, if( !(x == y))и здесь проверяется условие, а #Expr расширяется в строковый литерал "x == y", который мы затем помещаем в сообщение об ошибке.
Евгений Магдалиц

К сожалению, это решение вызывает неопределенное поведение из-за использования зарезервированных идентификаторов.
Помните Монику

19
BOOST_ASSERT_MSG(expre, msg)

http://www.boost.org/doc/libs/1_51_0/libs/utility/assert.html

Вы можете использовать это напрямую или скопировать код Boost. Также обратите внимание, что утверждение Boost - это только заголовок, поэтому вы можете просто получить этот единственный файл, если не хотите устанавливать все Boost.


@Jichao, что вы имеете в виду, говоря о реализации интерфейса assert?
Tarc

8

Поскольку ответ zneak несколько запутывает код, лучший подход - просто прокомментировать текст строки, о котором вы говорите. т.е. .:

assert(a == b); // A must be equal to B

Поскольку читатель ошибки assert все равно будет искать файл и строку из сообщения об ошибке, они увидят здесь полное объяснение.

Потому что, в конце концов, это:

assert(number_of_frames != 0); // Has frames to update

читается лучше, чем это:

assert(number_of_frames != 0 && "Has frames to update");

с точки зрения человеческого анализа кода, т.е. читаемость. Также не языковой взлом.


1
«Поскольку читатель сообщения об ошибке assert все равно будет искать файл и строку в сообщении об ошибке», - только если они прилежны.
Джейсон С.

Только если они хотят исправить ошибку, вы имеете в виду ... какой глупый комментарий
метаморфоза

1
Нет. Чем проще вы сделаете так, чтобы люди увидели проблему, тем больше вероятность, что они примут меры.
Джейсон С.

пожимать плечами Не согласен.
метаморфоза

2

assert - это комбинация макроса / функции. вы можете определить свой собственный макрос / функцию, используя __FILE__, __BASE_FILE__и __LINE__т. д., со своей собственной функцией, которая принимает настраиваемое сообщение


-6

Для vc добавьте следующий код в assert.h,

#define assert2(_Expression, _Msg) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Msg), _CRT_WIDE(__FILE__), __LINE__), 0) )

11
Изменение заголовков вашего компилятора - плохая идея.
Росс Ридж
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.