TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #772 4.42K
​​Засовываем исключение в исключение
#опытным

Вы знали, что в плюсах есть вложенные исключения? Такие исключения могут хранить в себе несколько исключений. Сегодня мы посмотрим, что это за зверь такой.

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

void ComplicatedCalculations() try {
// use db
} catch (std::exception& ex) {
std::throw_with_nested(std::runtime_error("Complicated Calculations Error"));
}

void HandlingCalculations() try {
ComplicatedCalculations();
} catch (std::exception& ex) {
std::throw_with_nested(std::runtime_error("Handling Calculations Error"));
}


Теперь исключение, которое вылетит из HandlingCalculations будет на самом деле содержать 3 исключения: от базы данных, от ComplicatedCalculations и от HandlingCalculations.

Вложенные исключения существуют с С++11 и очень интересно устроены. Рассмотрим несколько упрощенные версии сущностей, которые находятся под капотом механизма вложенных исключений. Есть класс std::nested_exception:

class nested_exception
{
exception_ptr _M_ptr;
public:
/// The default constructor stores the current exception (if any).
nested_exception() noexcept : _M_ptr(current_exception()) { }
...
};


Этот класс ответственен за захват текущего исключения с помощью вызова std::current_exception().

Дальше имеется класс, который хранит в себе все множество исключений:

template<typename Except>
struct Nested_exception : public Except, public nested_exception
{
explicit Nested_exception(const Except& ex)
: Except(ex) { }
};


Объекты этого класса наследуются от nested_exception, в котором захвачено старое исключение, и от Except - нового исключения.

Ну и последний компонент - std::throw_with_nested:

template<typename Tp>
[[noreturn]]
inline void throw_with_nested(Tp&& t)
{
throw Nested_exception<remove_cvref_t<Tp>>{std::forward<Tp>(t)};
}


При вызове throw_with_nested создается объект Nested_exception на основе переданного типа исключения и, неявно, nested_exception, которых сохраняет в себе указатель на старое исключение.

Получается, что мы при каждом вызове throw_with_nested подмешиваем новое исключение к старому с помощью множественного наследования.

Очень прикольная техника, которая позволяет строить цепочки объектов. Это как тупл, только расширяемый в рантайме.

Это все хорошо и интересно. Прокидывать вложенные исключения мы научились. Но рано или поздно их придется обработать. Как это сделать? Об этом будем говорить в следующем посте.

Inherit knowledge from your ancestor. Stay cool.

#cppcore #cpp11
  • 🔥 17
  • ❤ 9
  • 👍 5
More from @grokaemcpp
  1. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  2. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  3. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  4. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  5. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  6. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →