Утверждение с динамическим сообщением?

В моей программе я хочу использовать утверждения, отображающие сообщение об ошибке. Помимо известных обходных решений для C и С++ там "реальное" решение как BOOST предлагает BOOST_ASSERT_MSG( expr, msg ) (см. Также assert() с сообщением)

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

BOOST_ASSERT_MSG( length >= 0, "No positive length found! It is " << length )

Как вы можете видеть, я хотел бы отформатировать сообщение "строка" как stringstream или ostream, так как это позволило бы мне легко показать пользовательские типы (при условии, что я определил соответствующую функцию форматирования).

Проблема в том, что BOOST_ASSERT_MSG по умолчанию требует char const *, поэтому он несовместим.

Есть ли способ переопределить/перегрузить assertion_failed_msg() таким образом, что использование потока как сообщения будет работать? Как?
(Мой наивный подход потерпел неудачу, поскольку компилятор сначала захотел сделать operator<<("foo",bar) в самом сообщении...)

Ответ 1

Вы можете определить свой собственный макрос

#define ASSERT_WITH_MSG(cond, msg) do \
{ if (!(cond)) { std::ostringstream str; str << msg; std::cerr << str.str(); std::abort(); } \
} while(0)

Ответ 2

Это относительно тривиально для достижения этого.

BOOST_ASSERT_MSG( length >= 0, (std::stringstream() << "No positive length found! It is " << length).str().c_str() )

Ответ 3

Я использую BOOST_ASSERT_MSG с моей собственной оболочкой вокруг него, так что указание сообщения assert с несколькими operator<< кажется менее сложным.

#if defined ASSERT_ENABLED 

    #define ASSERT(cond, msg) {\
        if(!(cond))\
        {\
            std::stringstream str;\
            str << msg;\
            BOOST_ASSERT_MSG(cond, str.str().c_str());\
        }\
    }
#else
    #define ASSERT(...) 
#endif

укажите настраиваемое сообщение, которое вы выводите на cout:

  ASSERT(execSize == (_oldSize - remaining), "execSize : " << execSize << ", _oldSize : " << _oldSize << ", remaining : " << remaining);

Что он делает, если ASSERT_ENABLED определено, включите сообщения об утверждении. if(!(cond)) part - это оптимизация, которая позволяет избежать дорогостоящих операций с строкой, заданных параметром макроса msg, если cond - true

Ответ 4

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

template<typename Fn> 
void assert_fn( bool expr, Fn fn) {
  if (!expr) {
    fn();
    abort();
  }
}

Аргумент fn может быть любым вызываемым.
Например, вы можете назвать это так:

assert_fn( a==b, [&](){ cout << "Assertion failed: a="<< a << 
                                " is different from but b=" << b << endl; } ); 

Преимущество состоит в том, что вывод состоит в том, что вы не вызываете abort явно, и вывод полностью настраивается. Этим преимуществом, конечно же, являются семь дополнительных символов шаблона лямбда-функции: [&](){})