Подтвердить что ты не робот

С++ 11. Идиомы и передовая практика изменились?

Мне интересно, с тех пор как С++ 11, где были добавлены исключения между потоками и добавлены вложенные исключения, идиомы, измененные для захвата исключений, в целом.

Теперь мы имеем:

  • std::rethrow_if_nested
  • std::rethrow_with_nested
  • std::rethrow_exception
  • std::current_exception

Вложенные исключения должны использоваться, чтобы не потерять контекст для исключений.

Итак, теперь вы можете сделать что-то вроде этого:

void open_file(std::string const & file_name) {
   try {
      std::ifstream file;
      file.exceptions(ios::failbit | ios::badbit);
      file.open(file_name);
   }
   catch (...) {
      std::rethrow_with_nested(std::logic_error("File " + file_name + 
      " could not be open"));
   }
}

Вы можете получить backtrace, как это, если я не ошибаюсь:

void print_backtrace(std::exception const & e, int depth = 0) {
   std::cerr << std::string(depth, ' ') << e.what() << std::endl;
   try {
      std::rethrow_if_nested(e);
   }
   catch (std::exception const & ex) {
      print_backtrace(ex, ++depth);
   }
}

Итак, если вы используете print_backtrace с open_file, он должен предоставить вам std::logic_error + the ios_base::failure на выходе.

Мои вопросы:

  • Является ли эта идиома "правильным" способом обработки исключений в С++ 11, учитывая, что я хочу фиксировать все исключения без потери контекста?
  • Есть ли способ в print_backtrace для захвата исключений с помощью catch (...) для полного захвата?
  • Я не знаю, почему требуется std::rethrow_exception, и я не знаю, когда.
4b9b3361

Ответ 1

1. Я не знаю, что я назвал бы это идиомой. Если "правильно" означает "что-то похожее на то, как std::vector является" правильным "контейнером для использования по умолчанию, я не думаю, что действительно существует определенный" правильный "способ обработки ошибок. Это правильный способ в том, что он четко определяет поведение.

2. Сначала вы должны вызвать print_backtrace() в контексте, который не ограничивается некоторыми исключениями, что означает, что вы должны вызвать его в блоке catch(...):

    try {
      run();
    } catch(...) {
      print_backtrace();
    }

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

void print_backtrace(int depth = 0) {
  try {
    throw;
  }

  // this block shows how to handle exceptions of some known type
  // You can have your own types instead of std::exception
  catch (const std::exception & e) {
    std::cerr << std::string(depth, ' ') << e.what() << std::endl;
    try {
      std::rethrow_if_nested(e);
    }
    catch (...) {
      print_backtrace(++depth);
    }
  }

  // Not all nesting exceptions will be of a known type, but if they use the
  // mixin type std::nested_exception, then we can at least handle them enough to
  // get the nested exception:

  catch (const std::nested_exception & ne) {
    std::cerr << std::string(depth, ' ') << "Unknown nesting exception\n";

    try {
      ne.rethrow_nested();
    }
    catch (...) {
      print_backtrace(++depth);
    }
  }

  // Exception nesting works through inheritance, which means that if you
  // can't inherit from the type, then you can't 'mixin' std::nesting exception.
  // If you try something like std::throw_with_nested( int{10} ); Then you'll
  // hit this catch block when printing the backtrace.

  catch (...) {
    std::cerr << std::string(depth, ' ') << "Unknown exception\n";
  }
}

3. std::rethrow_exception используется с std::exception_ptr, который является типом, который может использоваться для переноса произвольного исключения. Единственный способ получить это исключение - использовать обычный механизм обработки исключений для исключения catch, что означает, что вы должны иметь возможность выбросить это исключение. То, что делает rethrow_exception. Это можно использовать для переноса произвольного исключения из одного потока в другой (как это делает std::future) или для хранения произвольного исключения в качестве члена (как std::nested_exception) или для передачи произвольного исключения в качестве параметра для которая попытается напечатать некоторое описание исключения.

void print_backtrace(std::exception_ptr e) {
  try {
    std::rethrow_exception(e);
  }

  catch (const std::exception & e) {
    // ...