Исключения внутри исключений в C ++ - PullRequest
10 голосов
/ 05 июля 2011

При работе с C # или Java я использовал для создания классов исключений, которые включали другие исключения в качестве членов класса. Например:

public class MyException : Exception {
    private MyException ex;
    private String message;

    public String what() {
        return this.message;
    }

    public String stack() {
        if(ex != null) {
            StringBuilder s;
            s.append(this.what());
            s.append(this.ex.stack());
            return s.toString();
        }

        return this.what();
    }
}

Я искал примеры по той же теме, но для C ++, и не смог найти ни одного (возможно, я не искал правильные термины, как вы видите, заголовок этого вопроса не очень причудлив).

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

РЕДАКТИРОВАТЬ: Возможно, то, что я написал, было немного запутанным или не очень хорошо известной (принятой) практикой программирования. Поэтому я укажу, как я собирался использовать этот класс с фрагментом:

 try {
     // Code that throws an exception of type MyException
 } catch(MyException ex) {
     MyException another = new MyException();
     another.setEx(ex);
     another.setMessage("A message that explains where the other exception was caught and on what conditions");

     throw another;
 }

Ответы [ 3 ]

21 голосов
/ 05 июля 2011

Нет правильного способа сделать это со стандартными исключениями в C ++ 03, потому что они предназначены для полиморфного использования, но не могут быть клонированы.Так что если вы поймаете std::exception const& e, вы сможете сохранить копию, но это приведет к разрезанию, потере всей полезной информации.Вы должны не хранить указатель или ссылку на исключение, потому что его время жизни заканчивается, как только выходит из предложения catch (при условии, что вы не выбрасываете исходное исключение).

Вы могли бы обойти это ограничение, если бы знали каждый тип, который можно бросить, и тестировали их все, но это не очень хороший дизайн (то есть он подрывает полиморфизм).Имеет смысл написать собственный базовый класс исключений, который можно клонировать, и поймать его.У вас все еще есть проблема, если вы поймаете std::exception, полученный из чужого кода.

В этот момент я вынужден упомянуть Boost.Exception .Это облегчает написание вашей собственной иерархии исключений и предоставляет различные утилиты, среди которых boost::exception_ptr.Затем вы можете сделать:

typedef boost::error_info<struct tag_nested_exception, boost::exception_ptr>
    nested_exception;

// ...
catch(...) {
    // better: use BOOST_THROW_EXCEPTION
    throw your_exception_type() << nested_exception( boost::current_exception() );
}

Это настолько полезно, что boost::diagnostic_info поддерживает его и отображает для вас вложенное исключение (оно недокументировано).Было даже предложено, чтобы этот nested_exception typedef тоже был частью библиотеки;тем временем это легко написать самостоятельно.

Не ожидайте магии, хотя: boost::current_exception 'захватывает' активное исключение (мелкий шрифт или его клон), хорошо, только если использовался сайт броска boost::enable_current_exception.(Функционально это моральный эквивалент использования базового класса исключений, который можно клонировать).Если этого не произойдет, это не даст сбоя, но некоторая информация может быть потеряна.


В качестве последнего замечания, знайте, что дизайн Boost.Exception был принят для C ++ 0x.Таким образом, следующее правильно сохраняет активное исключение, ни с одним из предупреждений boost::current_exception, так как оно имеет языковую поддержку:

// you can still use Boost.Exception:
typedef boost::error_info<struct tag_nested_exception, std::exception_ptr>
    nested_exception;

// ...
catch(...) {
    // e has type std::exception_ptr
    auto e = std::current_exception();
    // internally store the std::exception_ptr
    throw your_exception_type(e);

    // or with Boost
    BOOST_THROW_EXCEPTION( your_exception_type() << nested_exception(e) );
}

Существует также тип std::nested_exception, который можно очень легко использовать, например, так:

catch(...) {
    // throws an unspecified type derived from your_exception_type
    // and std::nested_exception
    std::throw_with_nested(your_exception_type());
}
1 голос
/ 05 июля 2011

Полагаю, вы могли бы бросить unique_ptr<some_exception>, а затем сделать inner_exception еще один unique_ptr, который станет владельцем.

0 голосов
/ 05 июля 2011

Я не думаю, что нам нужно свойство экземпляра класса (т.е. ex) в Java / C # / C ++, если ваш класс не разработан в одноэлементном шаблоне.Также, здесь - это учебник, на который вы, возможно, захотите взглянуть.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...