<system_error> категории и стандартные / системные коды ошибок

C++11 представил <system_error> заголовок, содержащий общую систему для обработки кодов ошибок. std::error_code это кортеж, содержащий int, код ошибки и ссылка на std::error_category, который определяет область ошибок и обработку кода ошибки. Стандартная библиотека поставляется с четырьмя категориями: std::generic_category, std::system_category, std::future_category, а также std::iostream_category,

Существуют конфликты по поводу того, какую категорию использовать, как здесь, на SO, так и на ссылочных сайтах C++, при создании std::error_code с / метательным std::system_error с errno и коды ошибок WinAPI:

Тем не мение, errno а также GetLastError() нельзя использовать одну и ту же категорию, иначе некоторые коды ошибок будут неоднозначными. Код ошибки 33 является одним из примеров, так как он EDOM а также ERROR_LOCK_VIOLATION,

Есть даже некоторые места, защищающие пользовательскую категорию для WinAPI, но я не могу найти никаких ссылок на это в данный момент. Эта альтернатива была бы особенно болезненной.

Какая категория должна использоваться с errno и который следует использовать с GetLastError() чтобы

  • std::error_code::default_error_condition()
  • std::error_code::message()

являются однозначными и соответствуют основному коду ошибки?

3 ответа

Решение

В стандарте C++:

system_category

В текущем проекте C++17 говорится, что:

Некоторые функции в стандартной библиотеке C++ сообщают об ошибках через std::error_code (19.5.2.1) объект. Этот объект category() член должен вернуться std::system_category() для ошибок, происходящих из операционной системы, или ссылки на определенный реализацией error_category объект для ошибок, возникающих в другом месте. Реализация должна определить возможные значения value() для каждой из этих категорий ошибок>. [Пример: для операционных систем, основанных на POSIX, реализации рекомендуется определить std::system_category() значения идентичны POSIX errno значения, с дополнительными значениями, как определено в документации операционной системы. Реализации для операционных систем, которые не основаны на POSIX, рекомендуется определять значения, идентичные значениям операционной системы. Для ошибок, которые не происходят из операционной системы, реализация может предоставить перечисления для связанных значений.

Это не так понятно

  • что должно случиться с errno значения на винде?

  • является errno из вызова POSIX "происходящего из операционной системы" или это должно быть ограничено вызовами не POSIX?

generic_category

  • std::errc перечисление с теми же значениями, что и в C/POSIX EFOOBAR код ошибки;

    Ценность каждого enum errc константа должна быть такой же, как значение <cerrno> макрос показан в приведенном выше резюме. Независимо от того, подвергает ли реализация <cerrno> макросы не указаны.

  • make_error_code(std::errc) генерирует erro_code с помощью generic_category

    error_code make_error_code(errc e) noexcept;

    Возвращает: error_code(static_cast<int>(e), generic_category()),

Это означает, что код ошибки POSIX можно использовать с generic_category, Не POSIX значения могут работать неправильно generic_catgeory, На практике они, кажется, поддерживаются реализациями, которые я использовал.

В ускорении

Усилить саму систему

Документация Boost довольно краткая об этой функции:

В первоначальном предложении категории ошибок рассматривались как двоичный выбор между ошибкой (то есть в стиле POSIX) и кодами ошибок операционной системы.

Кроме того, вы можете найти устаревшие декларации, такие как:

static const error_category & errno_ecat = generic_category();

В linux_error.hpp:

Чтобы создать код ошибки после ошибки API: error_code( errno, system_category() )

В windows_error.hpp:

Чтобы создать код ошибки после ошибки API: error_code( ::GetLastError(), system_category() )

В cygwin_error.hpp:

Чтобы создать код ошибки после ошибки API: error_code( errno, system_category())

Для Windows Boost использует system_category для не errno ошибки:

ec = error_code( ERROR_ACCESS_DENIED, system_category() );
ec = error_code( ERROR_ALREADY_EXISTS, system_category() );
ec = error_code( ERROR_BAD_UNIT, system_category() );
ec = error_code( ERROR_WRITE_PROTECT, system_category() );
ec = error_code( WSAEWOULDBLOCK, system_category() );

В ASIO

Мы находим такой код в ASIO:

template <typename ReturnType>
inline ReturnType error_wrapper(ReturnType return_value,
    boost::system::error_code& ec)
{
#if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__)
  ec = boost::system::error_code(WSAGetLastError(),
      boost::asio::error::get_system_category());
#else
  ec = boost::system::error_code(errno,
      boost::asio::error::get_system_category());
#endif
  return return_value;
}

Мы нашли errno как system_category в коде POSIX:

int error = ::pthread_cond_init(&cond_, 0);
boost::system::error_code ec(error,
    boost::asio::error::get_system_category());

Файловая система

Мы нашли errno с generic_category в коде POSIX:

if (::chmod(p.c_str(), mode_cast(prms)))
{
  if (ec == 0)
    BOOST_FILESYSTEM_THROW(filesystem_error(
      "boost::filesystem::permissions", p,
      error_code(errno, system::generic_category())));
  else
    ec->assign(errno, system::generic_category());

}

В GNU libstdC++

Файловая система

Мы нашли errno с generic_category:

if (char* rp = ::realpath(pa.c_str(), buf.get())) {
  [...]
}
if (errno != ENAMETOOLONG) {
  ec.assign(errno, std::generic_category());
  return result;
}

и не использовать system_category,

Использование libstdC++

На практике, кажется, вы можете использовать generic_category для не POSIX errno с помощью libstdC++:

std::error_code a(EADV, std::generic_category());
std::error_code b(EADV, std::system_category());
std::cerr << a.message() << '\n';
std::cerr << b.message() << '\n';

дает:

Advertise error
Advertise error

LibC++

Мы нашли errno с system_category:

int ec = pthread_join(__t_, 0);
if (ec)
  throw system_error(error_code(ec, system_category()), "thread::join failed");

но не использовать generic_category,

Заключение

Я не нахожу здесь никакой закономерности, но, видимо,

  • вы должны использовать system_category при использовании Windows ошибка в Windows;

  • вы можете безопасно использовать generic_category для значений POSIX errno;

  • вы не должны быть в состоянии использовать std::generic_category для не POSIX долин errno (это может не сработать);

  • Если вы не хотите проверить, если ваш errno значение POSIX: в системах на основе POSIX вы, как ожидается, сможете использовать system_error с errno (Строго говоря, поддержка этого не является обязательной, только поощряется). в системах на основе POSIX вы можете использовать system_error с errno,

Я должен признаться в некотором удивлении по поводу путаницы в отношении , поскольку Крис кратко изложил, как именно он работает на http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-1.html и лично я нахожу стандартный текст C++ выше совершенно ясным. Но подведем итоги очень краткими словами:

Если на POSIX:

generic_category => POSIX стандартное ошибочное пространство

system_category => Локальное пространство POSIX errno (обычно расширяет POSIX проприетарными кодами errno). использование strerror() расширить коды в строковые описания, возвращаемые message(),

На практике в POSIX обе реализации одинаковы и отображают собственное ошибочное пространство.

Если в Windows:

generic_category => POSIX стандартное ошибочное пространство, которое возвращается различными функциями эмуляции POSIX в MSVCRT, например fopen() так далее

system_category => Win32 GetLastError() пространство. использование FormatMessage() расширить коды в строковые описания, возвращаемые message(),

Как использовать переносимо

std::error_code ec;
#ifdef _WIN32
if((HANDLE)-1 == CreateFile(...))
  ec = std::error_code(GetLastError(), std::system_category());
#else
if(-1 == open(...))
  ec = std::error_code(errno, std::system_category());
#endif
// To test using portable code
if(ec == std::errc::no_such_file_or_directory)
   ...
// To convert into nearest portable error condition (lossy, may fail)
std::error_condition ec2(ec.default_error_condition())

Другие мысли:

Некоторые комментаторы говорят, что плохо спроектирован и не должен использоваться. Это просто неверно, это довольно оптимально, учитывая идиоматическую практику C++ 03 того времени, когда он разрабатывался, он генерирует очень жесткий высококачественный код с фиксированной задержкой на всех основных STL, кроме Dinkumware. Он расширяется пользователем для любой системы кодов произвольных ошибок и стандартизирует объединение в единую систему обработки ошибок сторонней библиотеки.

Правда, сегодня это выглядело бы совсем иначе, если бы глобальные переменные constexpr были доступны во время его разработки, и, возможно, это могло бы быть исправлено в стандарте C++ после 17 лет. Но если вы программист, которому нужно перемещаться по кодам ошибок из сторонние библиотеки без потери информации через код, не написанный, чтобы знать об этих сторонних библиотеках, тогда является отличным решением.

Считайте, что это похоже на virtual Ключевое слово для обработки кодов ошибок сторонней библиотеки - оно устраняет необходимость понимания кода сторонними кодами. Если у вас есть эта проблема в вашей кодовой базе - а большинство больших кодовых баз - - то, безусловно, вы должны использовать вместо любой системы отображения кода ошибки или системы перевода, которую вы используете в настоящее время.

Вот такая путаница!

Категория ошибки является источником ошибок. Библиотека iostreams выдает свои ошибки, поэтому у нее есть своя категория. Точно так же WinAPI является собственным источником ошибок, поэтому ТРЕБУЕТ определяемую пользователем категорию ошибок.

и оба для значений errno. Различие между ними основано на значении errno:

  • значения, указанные в POSIX, используются с generic_category() для создания переносимых
  • значения с эквивалентом POSIX переводятся и используются с generic_category()
  • значения, предоставленные реализацией операционной системы, за пределами значений POSIX, используются с system_category() -- для создания s, которые не являются переносимыми

The <system_error>библиотека построена вокруг (подразумеваемого) различия между и . Два класса имеют одни и те же методы и интерфейсы, за исключением:

  • объекты предназначены для "низкоуровневых" и системных ошибок
  • объекты являются переносимыми ошибками, например, определенными стандартом
  • они используют разные объекты, поскольку их числовые значения преобразуются в разные строки ошибок.
  • вы можете попытаться сопоставить системную специфику с переносным устройством с помощью default_error_condition()метод либо на, либо на его .

Однако при отсутствии перевода с error_codeк (или ни один из них не реализован) вы получаете на основе специфичного для системы , что уже противоречит цели переносимого сопоставления. Таково состояние стандартной библиотеки C++, я думаю...!

error_categoryобъекты знают, представляют ли они переносимые значения или непереносимые значения. определяет специфические для системы значения (из errno) и переводит их в значения POSIX (если они доступны), а затем использует generic_category()чтобы сопоставить такие значения с error_condition.

Каждая категория умеет переводить числовую ошибку в строку (описание). system_category()переводит конкретные ęrrnoзначения в строки, скорее всего, с использованием стандартных strerror()или же sys_errlist.

Отсюда следует, что использование значений WinAPI из ::GetLastError()с этой категорией является ошибкой программирования.

Например, используя WinSock2 API, который предоставляет значения ошибок из ::WSAGetLastError()функция требует еще одну категорию ошибок.

Другие вопросы по тегам