Добавить собственные сообщения в assert?

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

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

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

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


person Killrazor    schedule 11.09.2010    source источник
comment
Вы можете определить макрос, например this.   -  person HelloGoodbye    schedule 18.12.2015


Ответы (9)


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

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

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

person zneak    schedule 11.09.2010
comment
Другой вариант - поменять местами операнды и использовать оператор запятой. Вам нужны дополнительные круглые скобки, чтобы запятая не рассматривалась как разделитель между аргументами: assert(("A must be equal to B", a == b)); - person Keith Thompson; 08.01.2012
comment
Однако было бы неплохо иметь возможность печатать значения переменных, например: assert(a == b && "A (" << A << ") is not equal to B (" << B << ")"); - person Frank; 03.07.2013
comment
@Frank, printf возвращает ненулевое значение, если он что-то напечатал, поэтому вы можете сделать что-то вроде assert(a == b && printf("a (%i) is not equal to b (%i)", a, b)), хотя в этот момент вам, вероятно, следует написать свою собственную оболочку assert. - person zneak; 04.07.2013
comment
Плохой код! Я этого не понимаю! Если a == b ложно, выражение and также должно быть ложным, и, следовательно, строка не должна оцениваться. - person ragnarius; 07.11.2014
comment
@ragnarius, здесь применяется закон де Моргана, и поскольку строка всегда принимает значение true, она не может изменить результат условия. Это в исходном коде только как аннотация, поскольку макрос assert печатает всю строку assert в случае сбоя. - person zneak; 07.11.2014
comment
@zneak assert у меня не работает, я использую `Assert :: AreEqual (2, HelloWorld :: getTwo ());` можно ли применить аналогичный принцип к этому синтаксису? - person Wakan Tanka; 26.10.2016
comment
@WakanTanka, нет, тебе нужен контроль над состоянием. - person zneak; 26.10.2016
comment
@zneak извините, но я не понимаю, я следую этому руководству codeproject.com/Tips/1085171/ не могли бы вы опубликовать ссылку, которая проясняет вашу рекомендацию. Спасибо - person Wakan Tanka; 26.10.2016
comment
@WakanTanka, я ничего не рекомендую; вы не можете использовать этот обходной путь для сообщения. Это работает, потому что a == b && "foo" совпадает с a == b. Если ваш код сам не выполняет этого сравнения, вы не сможете этого сделать. - person zneak; 27.10.2016
comment
Разве что-то вроде assert(a==b ||(0&&"A is not equal to B")); не было бы лучше, поскольку это позволяет программе не оценивать строку каждый раз, когда она проходит через assert? - person TUI lover; 09.08.2019
comment
@TUIlover, это не то, как работают строковые литералы C; они являются константами времени компиляции, и их использование в этом контексте тривиально оптимизируется. Нет затрат на время выполнения. - person zneak; 09.08.2019

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

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

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

person Andrei Bozantan    schedule 16.07.2012
comment
Это отличный подход, с одной крошечной проблемой, он будет отображать предупреждение: левый операнд оператора запятой не действует при компиляции в g ++ с `-Wunused-value - person v010dya; 30.01.2016
comment
или с помощью макроса: #ifndef m_assert #define m_assert (expr, msg) assert ((msg, expr)) #endif - person Szymon Marczak; 03.08.2017
comment
Использование оболочки макроса позволяет избежать предупреждения gcc: #define m_assert(expr, msg) assert(( (void)(msg), (expr) )) - person Jander; 24.09.2018

Вот моя версия макроса 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

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

person Eugene Magdalits    schedule 16.05.2016
comment
Хороший. Очень полезно - person Killrazor; 03.06.2016
comment
Я немного запутался. Считается ли #Expr строкой для прямой подстановки? В чем разница между #Expr и Expr? - person Minh Tran; 22.09.2016
comment
@MinhTran Предположим, что ваше условие assert x == y. Затем Expr расширится до if( !(x == y)), и здесь проверяется условие, а #Expr расширится до строкового литерала "x == y", который мы затем поместим в сообщение об ошибке. - person Eugene Magdalits; 14.10.2016
comment
К сожалению, это решение вызывает неопределенное поведение из-за использования зарезервированных идентификаторов. - person Remember Monica; 12.03.2020

BOOST_ASSERT_MSG(expre, msg)

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

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

person Zero    schedule 09.10.2012
comment
@Jichao, что вы имеете в виду, говоря о реализации интерфейса assert? - person Tarc; 23.08.2016

Поскольку ответ 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");

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

person metamorphosis    schedule 18.11.2014
comment
Поскольку читатель сообщения об ошибке assert все равно будет искать файл и строку из сообщения об ошибке - только если они будут стараться. - person Jason S; 27.09.2017
comment
Только если они хотят исправить ошибку, вы имеете в виду ... какой глупый комментарий - person metamorphosis; 27.09.2017
comment
Нет. Чем проще вы сделаете так, чтобы люди увидели проблему, тем больше вероятность, что они примут меры. - person Jason S; 27.09.2017
comment
пожать плечами Не согласен. - person metamorphosis; 29.09.2017

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

person Merlyn Morgan-Graham    schedule 11.09.2010

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

E.g.:

static bool arguments_must_be_ordered(int a, int b) {return a <= b;}

void foo(int a, int b)
{
    assert(arguments_must_be_ordered(a, b));
    // ...
}

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

Даже если assert не отключен для сборок выпуска, компилятор, скорее всего, встроит предикат, если он довольно тривиален.

Тот же подход можно использовать для сложных if условий, требующих комментария. Вместо комментария просто вызовите функцию предиката с самоописанием.

person Emile Cormier    schedule 19.02.2021
comment
Я знаю, что это не дает прямого ответа на вопрос, но, тем не менее, это подход, который следует учитывать. - person Emile Cormier; 19.02.2021

Вы также можете просто написать свою собственную функцию утверждения. Очень простой пример:

bool print_if_false(const bool assertion, const char* msg) {
    if(!assertion) {
        // endl to flush
        std::cout << msg << std::endl;
    }
    return assertion;
}

int main()
{
    int i = 0;
    int j = 1;
    assert(print_if_false(i == j, "i and j should be equal"));
    return 0;
}

поиграйте с кодом.

Утверждение читается как Assertion print_if_false(i == j, "i and j should be equal").

person User12547645    schedule 16.08.2020

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

#define assert2(_Expression, _Msg) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Msg), _CRT_WIDE(__FILE__), __LINE__), 0) )
person Jichao    schedule 22.10.2013
comment
Модификация заголовков вашего компилятора - плохая идея. - person Ross Ridge; 21.04.2015