Правильный/элегантный способ реализации цепочки исключений C++?C++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Правильный/элегантный способ реализации цепочки исключений C++?

Сообщение Anonymous »

Я хотел бы реализовать класс Exception в C++, который имитирует класс из .NET Framework (и в Java тоже есть что-то похожее), для следующих целей:
  • Цепочка исключений: я хотел бы реализовать концепцию «трансляции исключений», когда исключения, перехваченные на более высоких уровнях, оборачивают и «транслируют» исключения более низкого уровня, а также каким-то образом сохраняя эти исключения более низкого уровня (в элементе InnerException, в этом случай). Для этого должен быть какой-то механизм для хранения внутренних исключений вместе с каждым исключением, возникающим на верхнем уровне. Член InnerException обеспечивает это в реализации ниже.
  • Наследование исключений: должна быть возможность получить IoException из Exception и SerialPortException, например, из IoException. Хотя это кажется тривиальным, должна быть возможность динамически определять тип перехваченных исключений (например, для целей регистрации или для отображения пользователю), желательно без накладных расходов на RTTI и typeid.
Это пример логики обработки исключений, которую я хотел бы сделать возможным:

Код: Выделить всё

try
{
try
{
try
{
throw ThirdException(L"this should be ThirdException");
}
catch(Exception &ex)
{
throw SubException(L"this should be SubException", ex);
}
}
catch(Exception &ex)
{
throw SubException(L"this should be SubException again", ex);
}
}
catch(Exception &ex)
{
throw Exception(L"and this should be Exception", ex);
}
и при перехвате «самого внешнего» исключения на самом верхнем уровне я хотел бы иметь возможность анализировать и форматировать всю цепочку исключений через элемент InnerException, чтобы отображать что-то вроде этого:
Изображение

Я придумал следующую реализацию, поэтому далеко:
Небольшое примечание: CString — это строковый класс, специфичный для Microsoft (только для людей, не знакомых с Visual C++).

Код: Выделить всё

class Exception
{
protected:

Exception(const Exception&) {};
Exception& operator= (const Exception&) {};

public:

Exception(const CString &message) : InnerException(0), Message(message) {}
Exception(const CString &message, const Exception &innerException) : InnerException(innerException.Clone()), Message(message) {}

virtual CString GetExceptionName() const { return L"Exception"; }

virtual Exception *Clone() const
{
Exception *ex = new Exception(this->Message);
ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;
return ex;
}

public:

virtual ~Exception() { if (InnerException) delete InnerException; }

CString Message;
const Exception *InnerException;
};
Что же у нас здесь есть? Конструктор копирования и оператор присваивания защищены, чтобы предотвратить копирование. Каждый объект будет «владеть» своим внутренним объектом исключения (и удалять его в деструкторе), поэтому поверхностное копирование по умолчанию будет неприемлемо. Далее у нас есть два довольно стандартно выглядящих конструктора и виртуальный деструктор, удаляющий объект InnerException. Виртуальный метод Clone() отвечает за глубокое копирование объектов, в первую очередь за сохранение внутреннего объекта исключения (см. второй конструктор). И, наконец, виртуальный метод GetExceptionName() предоставляет дешевую альтернативу RTTI для идентификации имен классов исключений (не думаю, что это выглядит круто, но я не смог придумать лучшего решения; для сравнения: в .NET можно просто использовать someException.GetType().Name).
Теперь это делает свою работу. Но... Мне не нравится это решение по одной конкретной причине: объем кода, необходимый для каждого производного класса. Предположим, мне нужно получить класс SubException, который не предоставляет абсолютно никаких дополнений к функциональности базового класса, он просто предоставляет собственное имя («SubException», которое может быть «IoException», «ProjectException», ...), чтобы дифференцировать его для сценария использования. Мне нужно предоставить почти одинаковое количество кода для каждого такого класса исключений. Вот оно:

Код: Выделить всё

class SubException : public Exception
{
protected:

SubException(const SubException& source) : Exception(source) {};
SubException& operator= (const SubException&) {};

public:

SubException(const CString &message) : Exception(message) {};
SubException(const CString &message, const Exception &innerException) : Exception(message, innerException) {};

virtual CString GetExceptionName() const { return L"SubException"; }

virtual Exception *Clone() const
{
SubException *ex = new SubException(this->Message);
ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;
return ex;
}
};
Мне не нравится, что мне приходится каждый раз предоставлять защищенный конструктор копирования и оператор присваивания, мне не нравится, что мне приходится каждый раз клонировать метод Clone, дублируя даже код копирования базовых членов (

Код: Выделить всё

InnerException
...), просто... Я не думаю, что это элегантное решение. Но лучшего варианта я придумать не смог. Есть ли у вас идеи, как «правильно» реализовать эту концепцию? Или, может быть, это лучшая реализация этой концепции, возможная в C++? Или, может быть, я делаю это совершенно неправильно?
P.S.: Я знаю, что в C++11 (также в Boost) существуют некоторые механизмы для этой цели (цепочка исключений) с некоторыми новыми классами исключений, но меня в первую очередь интересуют специальные, "совместимые со старым C++" способы. Но кроме того, было бы хорошо, если бы кто-нибудь мог предоставить какой-нибудь код на C++11, который выполняет то же самое.

Подробнее здесь: https://stackoverflow.com/questions/133 ... n-chaining
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»