<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
сstd::generic_category
: Так ответьте, llvm- commits, cplusplus.comerrno
сstd::system_category
: ТАК ответ, cppreference.comGetLastError()
сstd::generic_category
: ТАК ответьGetLastError()
сstd::system_category
: ТАК ответь, ТАК комментарий
Тем не мение, 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()
значения идентичны POSIXerrno
значения, с дополнительными значениями, как определено в документации операционной системы. Реализации для операционных систем, которые не основаны на POSIX, рекомендуется определять значения, идентичные значениям операционной системы. Для ошибок, которые не происходят из операционной системы, реализация может предоставить перечисления для связанных значений.
Это не так понятно
что должно случиться с
errno
значения на винде?является
errno
из вызова POSIX "происходящего из операционной системы" или это должно быть ограничено вызовами не POSIX?
generic_category
std::errc
перечисление с теми же значениями, что и в C/POSIXEFOOBAR
код ошибки;Ценность каждого
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
для значений POSIXerrno
;вы не должны быть в состоянии использовать
std::generic_category
для не POSIX долинerrno
(это может не сработать);Если вы не хотите проверить, если ваш
errno
значение POSIX:в системах на основе POSIX вы, как ожидается, сможете использоватьв системах на основе POSIX вы можете использоватьsystem_error
сerrno
(Строго говоря, поддержка этого не является обязательной, только поощряется).system_error
сerrno
,
Я должен признаться в некотором удивлении по поводу путаницы в отношении
Если на 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())
Другие мысли:
Некоторые комментаторы говорят, что
Правда, сегодня это выглядело бы совсем иначе, если бы глобальные переменные 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()
функция требует еще одну категорию ошибок.