Исключение в деструкторе
От: Xchllataa  
Дата: 13.11.07 14:05
Оценка: :)
Здравствуйте

Для удобства в формировании сообщений при генерации исключений, написал следующий код
#include <iostream>
#include <sstream>
#include <stdexcept>

class Throw
{
public:
    Throw()
    {
    }

    ~Throw()
    {
        // Здесь предполагается выводить stream.str() в лог

        // C4722 'Throw::~Throw' : destructor never returns, potential memory leak
        throw std::runtime_error(stream.str());
    }

    operator std::ostream&() {return stream;}

private:
    std::ostringstream stream;

    Throw(Throw const&);
    Throw const& operator=(Throw const&);
};

// suppress C4127
inline bool false_() {return false;}

#define DBG_THROW() if (false_()) ; else static_cast<std::ostream&>(Throw()) << '[' << __FUNCTION__ << "] "

int main()
{
    try
    {
        DBG_THROW() << "test " << 42;
    }
    catch (std::exception const& e)
    {
        std::cout << e.what() << std::endl;
    }

    return 0;
}

Код работает, как ожидалось, но при компиляции в Release выдаётся следующее сообщение

warning C4722: 'Throw::~Throw' : destructor never returns, potential memory leak

Насколько в данном случае это опасно? Вроде при работе Throw::~Throw() все нужные деструкторы вызываются.

З.Ы.
Я знаю, что если Throw::~Throw() был вызван в результате другого исключение, то приложение упадёт, но здесь это вроде не проблема
Re: Исключение в деструкторе
От: elcste  
Дата: 13.11.07 14:41
Оценка: 1 (1)
Здравствуйте, Xchllataa, Вы писали:

Для удобства в формировании сообщений при генерации исключений, написал следующий код
X>#include <iostream>
X>#include <sstream>
X>#include <stdexcept>

X>class Throw
X>{
X>public:
X>    Throw()
X>    {
X>    }

X>    ~Throw()
X>    {
X>        // Здесь предполагается выводить stream.str() в лог

X>        // C4722 'Throw::~Throw' : destructor never returns, potential memory leak
X>        throw std::runtime_error(stream.str());
X>    }

X>    operator std::ostream&() {return stream;}

X>private:
X>    std::ostringstream stream;

X>    Throw(Throw const&);
X>    Throw const& operator=(Throw const&);
X>};

X>// suppress C4127
X>inline bool false_() {return false;}

X>#define DBG_THROW() if (false_()) ; else static_cast<std::ostream&>(Throw()) << '[' << __FUNCTION__ << "] "

int foo(int bar)
{
    if(bar == 0xdeadbeef)
        DBG_THROW() << "Oops...";
    return 42;
}

int main()
{
    try
    {
        DBG_THROW() << "test " << foo(0xdeadbeef);
    }
    catch (std::exception const& e)
    {
        std::cout << e.what() << std::endl;
    }
}

X>Я знаю, что если Throw::~Throw() был вызван в результате другого исключение, то приложение упадёт, но здесь это вроде не проблема

Угу.
Re[2]: Исключение в деструкторе
От: Xchllataa  
Дата: 13.11.07 15:25
Оценка:
Здравствуйте, elcste, Вы писали:

int foo(int bar)
{
    if(bar == 0xdeadbeef)
        DBG_THROW() << "Oops...";
    return 42;
}

int main()
{
    try
    {
        DBG_THROW() << "test " << foo(0xdeadbeef);
    }
    catch (std::exception const& e)
    {
        std::cout << e.what() << std::endl;
    }
}

E>Угу.
Однако в данном примере функция foo() вызывается до того, как будет создан первый временный объект Throw(), и в результате исключения внутри foo() он не будет создан вообще. Т.е. проблемы вроде и нет?
Re[3]: Исключение в деструкторе
От: elcste  
Дата: 13.11.07 15:34
Оценка:
Здравствуйте, Xchllataa, Вы писали:

X>int foo(int bar)
X>{
X>    if(bar == 0xdeadbeef)
X>        DBG_THROW() << "Oops...";
X>    return 42;
X>}

X>int main()
X>{
X>    try
X>    {
X>        DBG_THROW() << "test " << foo(0xdeadbeef);
X>    }
X>    catch (std::exception const& e)
X>    {
X>        std::cout << e.what() << std::endl;
X>    }
X>}

X>Однако в данном примере функция foo() вызывается до того, как будет создан первый временный объект Throw(), и в результате исключения внутри foo() он не будет создан вообще. Т.е. проблемы вроде и нет?

Порядок вычисления операндов не специфицирован. Так, на MSVC 2005 этот код падает в Release и не падает в Debug.
Re[4]: Исключение в деструкторе
От: Xchllataa  
Дата: 13.11.07 15:35
Оценка:
Здравствуйте, elcste, Вы писали:

E>Порядок вычисления операндов не специфицирован. Так, на MSVC 2005 этот код падает в Release и не падает в Debug.


Понятно, спасибо.
Re[3]: Исключение в деструкторе
От: Roman Odaisky Украина  
Дата: 13.11.07 15:36
Оценка:
Здравствуйте, Xchllataa, Вы писали:

E>>Угу.

X>Однако в данном примере функция foo() вызывается до того, как будет создан первый временный объект Throw(), и в результате исключения внутри foo() он не будет создан вообще. Т.е. проблемы вроде и нет?

Что вызовется раньше, не определено. Зависит от компилятора, контекста и фазы Луны.
До последнего не верил в пирамиду Лебедева.
Re: Исключение в деструкторе
От: Left2 Украина  
Дата: 13.11.07 16:23
Оценка:
Я бы делал так:

#define DBG_THROW(str) if (false_()) ; else { Throw x; x << '[' << __FUNCTION__ << "] " << str; x.DoThrow(); }

и потом

DBG_THROW("test " << 42);


Во-1, по синтаксису для конечного пользователя практически то же самое
Во-2, проще выключается в compile-time
В-3, не будешь раздражать компилятор бросанием исключений из деструктора
... << RSDN@Home 1.2.0 alpha rev. 717>>
Re[2]: Исключение в деструкторе
От: Xchllataa  
Дата: 14.11.07 07:19
Оценка:
Здравствуйте, Left2, Вы писали:

L>DBG_THROW("test " << 42);

L>Во-1, по синтаксису для конечного пользователя практически то же самое
L>Во-2, проще выключается в compile-time
L>В-3, не будешь раздражать компилятор бросанием исключений из деструктора

Спасибо за идею, сделаю что-то в этом роде
Re[2]: Исключение в деструкторе
От: Erop Россия  
Дата: 14.11.07 20:01
Оценка:
Здравствуйте, Left2, Вы писали:

L>DBG_THROW("test " << 42);


L>Во-1, по синтаксису для конечного пользователя практически то же самое

L>Во-2, проще выключается в compile-time
L>В-3, не будешь раздражать компилятор бросанием исключений из деструктора

Мне так тоже нравится, но могут быть проблемы, с конструкциями, вроде:
DBG_THROW( my_manager<char, traits<char> >::get_last_error_message() )


В принципе, если охота извращаться в стиле оригинального варианта, то можно сделать так:
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
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.