[≈РЕШЕНО] scope(failure) в C++ (реализация фишки из D, можно ли узнать тип вылетевшего исключения?..)

Модератор: Модераторы разделов

Аватара пользователя
gluk47
Сообщения: 297
Статус: Любитель гвоздей и микроскопов
ОС: Kubuntu 17.10

[≈РЕШЕНО] scope(failure) в C++

Сообщение gluk47 »

Добрый день присутствующим.

В проекте, который я пишу, используется автоматическое включение вывода в лог, если из функции класса вылетело исключение — все следующие вызовы к этому классу логгируются до некоторых более-менее хитрых условий остановки (эти критерии — отдельная тема, не важно). Сейчас это включение флагов — наполовину RAII, наполовину с ручным приводом. Хочется написать класс, который в деструкторе будет взводить bool debug, если деструктор вызван в процессе разматывания стека из-за исключения (if (std::uncaught_exception())).
Но исключения бывают разные: например, std::logic_error, std::bad_alloc и, среди прочих, std::runtime_error. Последние не должны взводить этот флаг, потому что runtime_error — не ошибка программы, её не надо отлаживать, это просто некорректные данные.

Можно ли как-то в процессе разматывания стека выяснить, какое же исключение является тем std::uncaught_exception()?

Можно говорить что-то вроде

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

auto_flag.Cancel();
throw runtime_error("bad data, sorry master!");

но это опять наполовину ручное решение — нужно каждый раз не забыть сказать флагу, чтобы он не взводился, а заодно и ловить все прилетающие такие исключения, отключать флаг и вызывать throw, что уж совсем бессмысленный лишний код.
Неужели в C++ нельзя до конца использовать RAII? :unsure:
[gluk47@gluk47-desktop ~]$ ензу куищще
куищще is aliased to `reboot'
Спасибо сказали:
Аватара пользователя
gluk47
Сообщения: 297
Статус: Любитель гвоздей и микроскопов
ОС: Kubuntu 17.10

Re: [≈РЕШЕНО] scope(failure) в C++

Сообщение gluk47 »

Как-то я забыл, что включать мозг — часто помогает.

Достаточно завести где-нибудь глобальную переменную

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

uint8_t& runtime_error_thrown() {
  static thread_local uint8_t _ =0;
  return _;
}


И класс

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

struct data_error : public std::runtime_error {
  data_error (const char* _) : runtime_error (_) { ++runtime_error_thrown(); }
  ~data_error() { --runtime_error_thrown(); }
};


Тогда класс, взводящий отладочный флаг, будет выглядеть просто так:

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

struct AutoRaiseDebugFlag {
  AutoRaiseDebugFlag(std::function<void()>&& _raise_flag) : _RaiseFlag(std::move(_raise_flag)) throw() {}
  AutoRaiseDebugFlag(AutoRaiseDebugFlag&& _) throw() : _RaiseFlag(_._RaiseFlag) {_._RaiseFlag = [](){}; }
  AutoRaiseDebugFlag& (const AutoRaiseDebugFlag&) = delete;
  AutoRaiseDebugFlag& operator= (const AutoRaiseDebugFlag&) = delete;
  ~AutoRaiseDebugFlag() { if (std::uncaught_exception and not runtime_error_thrown()) _RaiseFlag(); }
private:
  std::function<void()> _RaiseFlag;
};


Вызов класса — как-то так

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

struct MyClass {
  bool EnableDebug;
  inline AutoRaiseDebugFlag TrackErrors () throw() { return AutoRaiseDebugFlag([this] () { EnableDebug = true; }); }
  void MyFunction () {
    auto __trdb__ = TrackErrors();
        // ...
  }
};
[gluk47@gluk47-desktop ~]$ ензу куищще
куищще is aliased to `reboot'
Спасибо сказали: