E>Угу.
Однако в данном примере функция foo() вызывается до того, как будет создан первый временный объект Throw(), и в результате исключения внутри foo() он не будет создан вообще. Т.е. проблемы вроде и нет?
X>Однако в данном примере функция foo() вызывается до того, как будет создан первый временный объект Throw(), и в результате исключения внутри foo() он не будет создан вообще. Т.е. проблемы вроде и нет?
Порядок вычисления операндов не специфицирован. Так, на MSVC 2005 этот код падает в Release и не падает в Debug.
Здравствуйте, Xchllataa, Вы писали:
E>>Угу. X>Однако в данном примере функция foo() вызывается до того, как будет создан первый временный объект Throw(), и в результате исключения внутри foo() он не будет создан вообще. Т.е. проблемы вроде и нет?
Что вызовется раньше, не определено. Зависит от компилятора, контекста и фазы Луны.
#define DBG_THROW(str) if (false_()) ; else { Throw x; x << '[' << __FUNCTION__ << "] " << str; x.DoThrow(); }
и потом
DBG_THROW("test " << 42);
Во-1, по синтаксису для конечного пользователя практически то же самое
Во-2, проще выключается в compile-time
В-3, не будешь раздражать компилятор бросанием исключений из деструктора
Здравствуйте, Left2, Вы писали:
L>DBG_THROW("test " << 42); L>Во-1, по синтаксису для конечного пользователя практически то же самое L>Во-2, проще выключается в compile-time L>В-3, не будешь раздражать компилятор бросанием исключений из деструктора
L>Во-1, по синтаксису для конечного пользователя практически то же самое L>Во-2, проще выключается в compile-time L>В-3, не будешь раздражать компилятор бросанием исключений из деструктора
Мне так тоже нравится, но могут быть проблемы, с конструкциями, вроде:
В принципе, если охота извращаться в стиле оригинального варианта, то можно сделать так:
class MyError : public std::runtime_error {
public:
MyError( const char* text = "" ) : std::runtime_error( text ), state( constructed ) { accumulator << text; }
MyError( const MyError& other ) : std::runtime_error( other.accumulator.str() ), state( constructed ) {}
std::runtime_error ToStd() const { return std::runtime_error( accumulator.str() ); }
virtual const char *what() const
{
switch( state ) {
case constructed:
return std::runtime_error::what();
case buffered:
return buffer.c_str();
case modified:
buffer = accumulator.str();
state = buffered;
}
assert( false );
return"MyError internal error";
}
template<typename T>
MyError& operator << ( const T& t )
{
accumulator << t;
state = modified;
return *this;
}
private:
std::ostringstream accumulator;
mutable std::string buffer;
mutable enum {
constructed, // только что сконструирован
buffered, // изменён, но изменённый текст сохранён в буфер
modified // изменён, и буфер устарел
} state;
};
#define MY_ERROR() MyError( "[" __FUNCTION__ "] " )
Использование:
try {
throw MY_ERROR() << XXX << YYY << XX<X, Y>::LastErrorText();
} catch( MyError& e ) { // Можно так
e.what(); // ***1***
} catch( std::runtime_error& e ) { // или так
e.what();
} catch( std::exception& e ) { // или так
e.what();
} catch( std::runtime_error e ) { // или даже так (типа срезка)
e.what();
}
Единственное, чего нельзя --
MyError e( "Ups" );
e << "Losted text";
std::runtime_error rte( e ); // тут мы текст потеряем
std::runtime_error safe_rte( e.ToStd() ); // А так уже можно
Да, если кто не знает тонкостей, то вроде как гарантируется, что в точке ***1*** e.state будет MyError::constructed
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском