Должны ли исключения быть прикованными к C ++? [Дубликат

StackOverflow https://stackoverflow.com/questions/3543725

Вопрос

Я только что закончил работу на C ++ - программа, в которой я реализовал свои собственные исключения (хотя и происходит от STD :: Исключение). Практика, которую я применил, когда одно исключение вызывает цепную реакцию, размножая ошибку вверх и порождая к другим исключениям, состоит в том, чтобы объединить сообщение об ошибке на каждом подходящем шаге по модулям (классы чтения). Т.е. сам старое исключение упал, и создается новое исключение, но с более длинным сообщением об ошибке.

Возможно, это сработало для моей небольшой программы, но я не был очень доволен своим подходом в конце. Для одного, номера линии (хотя и не применяются в данный момент) и имена файлов не сохраняются, за исключением последнего исключения; И действительно эта информация наиболее интересна в первую очередь.

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

Это в конечном итоге приводит меня к рассмотрению, будь то ценные исключения в C ++ такая хорошая идея в конце концов. Возможно, нужно просто создать одно исключение, а затем добавить дополнительные данные к этому (как я делаю, но, вероятно, в гораздо лучшей манере)?

Какой твой ответ на это? Должны ли исключения, вызванные другой, чтобы сохранить своего рода «след исключения» - и как следует реализовать? - или следует использовать одно исключение, и дополнительные данные прилагаются к нему - и как это должно быть сделано?

Это было полезно?

Решение

Необходимо копировать данные из объекта исключения в цепочку, если вы хотите, чтобы он был переселен catch блок, который получает его, кроме Rethrow throw;. Отказ (Который включает, например, если это catch блок выходит через throw obj;.)

Это может быть сделано путем сохранения данных на кучу и реализация swap (move в C ++ 0x) на ваших личных данных внутри исключения, например.

Конечно, вам нужно быть осторожным при использовании кучи с исключениями ... но затем снова, в большинстве современных ОС, преодоление памяти полностью предотвращает new из когда-либо бросающих, к лучшему или к худшему. Хорошая маржа памяти и падение исключений из цепи при полном расплаве должна сохранять безопасную.

struct exception_data { // abstract base class; may contain anything
    virtual ~exception_data() {}
};

struct chained_exception : std::exception {
    chained_exception( std::string const &s, exception_data *d = NULL )
        : data(d), descr(s) {
        try {
            link = new chained_exception;
            throw;
        } catch ( chained_exception &prev ) {
            swap( *link, prev );
        } // catch std::bad_alloc somehow...
    }

    friend void swap( chained_exception &lhs, chained_exception &rhs ) {
        std::swap( lhs.link, rhs.link );
        std::swap( lhs.data, rhs.data );
        swap( lhs.descr, rhs.descr );
    }

    virtual char const *what() const throw() { return descr.c_str(); }

    virtual ~chained_exception() throw() {
        if ( link && link->link ) delete link; // do not delete terminator
        delete data;
    }

    chained_exception *link; // always on heap
    exception_data *data; // always on heap
    std::string descr; // keeps data on heap

private:
    chained_exception() : link(), data() {}
    friend int main();
};

void f() {
    try {
        throw chained_exception( "humbug!" );
    } catch ( std::exception & ) {
        try {
            throw chained_exception( "bah" );
        } catch ( chained_exception &e ) {
            chained_exception *ep = &e;
            for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
                std::cerr << ep->what() << std::endl;
            }
        }
    }

    try {
        throw chained_exception( "meh!" );
    } catch ( chained_exception &e ) {
        for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
            std::cerr << ep->what() << std::endl;
        }
    }
}

int main() try {
    throw chained_exception(); // create dummy end-of-chain
} catch( chained_exception & ) {
    // body of main goes here
    f();
}

Выход (соответственно сварливый):

bah
humbug!
meh!

Другие советы

Поскольку этот вопрос был задан, изменяемые изменения были сделаны в стандарте с C ++ 11. Я постоянно пропускаю это в обсуждениях об исключении, но следующий подход, гнездовые исключения, делает трюк:

Использовать std::nested_exception а также std::throw_with_nested

Это описано на стойке здесь а также здесь, как ты можешь Получить задницу на ваши исключения Внутри вашего кода без необходимости для отладчика или грохотного ведения журнала, просто написав правильный обработчик исключения, который будет воспроизведена вложенные исключения.

Поскольку вы можете сделать это с любым полученным классом исключения, вы можете добавить много информации на такой задний план! Вы также можете взглянуть на мой МВЕ на Github, где задний стол бы выглядел что-то подобное:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

Вы можете посмотреть на это: http://www.boost.org/doc/libs/1_43_0/libs/Exception/doc/boost-exception.html.

Это несколько разный подход к тому, что MS сделал в C #, но, похоже, соответствует вашим требованиям.

Другая идея состоит в том, чтобы добавить соответствующие данные в ваш объект исключения, затем используйте голый throw; заявление, чтобы перебросить его. Я думаю, что информация стека сохраняется в этом случае, и поэтому вы все равно будете знать оригинальный источник исключения, но тестирование будет хорошей идеей.

Могу поспорить, поскольку информация о стенке все будет доступна вообще, является определенной реализацией, что реализации будут варьироваться еще более широко в зависимости от того, сохраняется ли она каким-либо образом после throw; утверждение.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top