c++ - simbolo - error promedio
<system_error> categorías y códigos de error estándar/del sistema (2)
C ++ 11 introdujo el <system_error>
contiene un sistema genérico para manejar los códigos de error. Un std::error_code
es una tupla que contiene un int
, el código de error y una referencia a std::error_category
, que define el dominio de error y el manejo del código de error. La biblioteca estándar viene con cuatro categorías: std::generic_category
, std::system_category
, std::future_category
y std::iostream_category
.
Existen conflictos sobre qué categoría utilizar, tanto aquí en SO como en sitios de referencia de C ++, al crear std::error_code
s / throwing std::system_error
s con códigos de error errno
y WinAPI:
-
errno
constd::generic_category
: SO answer , llvm-commits , cplusplus.com -
errno
constd::system_category
: SO answer , cppreference.com -
GetLastError()
constd::generic_category
: SO answer -
GetLastError()
constd::system_category
: SO answer , SO comment
Sin embargo, errno
y GetLastError()
no pueden usar la misma categoría, de lo contrario algunos códigos de error serían ambiguos. El código de error 33 es un ejemplo, ya que es EDOM
y ERROR_LOCK_VIOLATION
.
Incluso hay algunos lugares que defienden una categoría hecha por el usuario para WinAPI, pero no puedo encontrar ninguna referencia a eso en este momento. Esta alternativa sería especialmente dolorosa.
Qué categoría se debe usar con errno
, y cuál se debe usar con GetLastError()
para que
-
std::error_code::default_error_condition()
-
std::error_code::message()
son ineficaces y apropiados para el código de error subyacente?
En el estándar de C ++:
system_category
El borrador actual de C ++ 17 establece que:
Ciertas funciones en la biblioteca estándar de C ++ informan errores a través de un objeto
std::error_code
(19.5.2.1). El miembrocategory()
ese objeto devolverástd::system_category()
para los errores que se originan en el sistema operativo, o una referencia a un objetoerror_category
definido por laerror_category
para los errores que se originan en otro lugar. La implementación debe definir los valores posibles de value () para cada una de estas categorías de error. [Ejemplo: para los sistemas operativos basados en POSIX, se recomienda a las implementaciones que definan los valores destd::system_category()
como idénticos a los valores de POSIXerrno
, con valores adicionales definidos por la documentación del sistema operativo. Se recomienda a las implementaciones para sistemas operativos que no están basados en POSIX que definan valores idénticos a los valores del sistema operativo. Para los errores que no se originan en el sistema operativo, la implementación puede proporcionar enumeraciones para los valores asociados.
No está tan claro:
¿Qué se supone que pasará con los valores
errno
en Windows?es un
errno
de una llamada POSIX "originado desde el sistema operativo" o se supone que está restringido a llamadas no POSIX?
generic_category
std::errc
es una enumeración con los mismos valores que el código de errores C / POSIXEFOOBAR
;El valor de cada constante
enum errc
será el mismo que el valor de la macro<cerrno>
muestra en la sinopsis anterior. No se especifica si la implementación expone las macros<cerrno>
.make_error_code(std::errc)
genera unerro_code
usandogeneric_category
error_code make_error_code(errc e) noexcept;
Devuelve:
error_code(static_cast<int>(e), generic_category())
.
Esto significa que el código de error POSIX se puede usar con generic_category
. Los valores no POSIX posiblemente no funcionen correctamente con generic_catgeory
. En la práctica, parecen ser compatibles con las implementaciones que he estado usando.
En Boost
Sistema Boost en sí mismo
La documentación de Boost es bastante escueta sobre esta característica:
La propuesta original consideraba las categorías de error como una opción binaria entre errno (es decir, estilo POSIX) y los códigos de error del sistema operativo nativo.
Además, puedes encontrar una declaración heredada como:
static const error_category & errno_ecat = generic_category();
En linux_error.hpp
:
Para construir un error_code después de un error de la API:
error_code( errno, system_category() )
En windows_error.hpp
:
Para construir un error_code después de un error de API:
error_code( ::GetLastError(), system_category() )
En cygwin_error.hpp
:
Para construir un error_code después de un error de la API: error_code (errno, system_category ())
Para Windows, Boost usa system_category
para errores no 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() );
En ASIO
Encontramos este tipo de código en 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;
}
Encontramos errno
como system_category
en el código POSIX:
int error = ::pthread_cond_init(&cond_, 0);
boost::system::error_code ec(error,
boost::asio::error::get_system_category());
Sistema de archivos
Encontramos errno
con generic_category
en código 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());
}
En GNU libstdc ++
Sistema de archivos
Encontramos errno
con generic_category
:
if (char* rp = ::realpath(pa.c_str(), buf.get())) {
[...]
}
if (errno != ENAMETOOLONG) {
ec.assign(errno, std::generic_category());
return result;
}
y sin uso de system_category
.
Usando libstdc ++
En la práctica, parece que puede usar generic_category
para no-POSIX errno
con 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'';
Da:
Advertise error
Advertise error
Libc ++
Encontramos errno
con system_category
:
int ec = pthread_join(__t_, 0);
if (ec)
throw system_error(error_code(ec, system_category()), "thread::join failed");
pero no hay uso de generic_category
.
Conclusión
No encuentro ningún patrón consistente aquí, pero aparentemente:
se espera que use
system_category
al usar el error de Windows en Windows;puede usar
generic_category
de formageneric_category
para los valores POSIX deerrno
;se supone que no se puede usar
std::generic_category
para valles no POSIX deerrno
(puede que no funcione);Si no desea verificar si su valor
errno
es un POSIX:en los sistemas basados en POSIX, se espera que pueda usaren sistemas basados en POSIX puede usarsystem_error
conerrno
(estrictamente hablando, el soporte para esto no es obligatorio, solo essystem_error
).system_error
conerrno
.
Debo admitir que me sorprendió un poco la confusión con respecto a <system_error> dado que Chris resumió exactamente cómo funciona en http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-1.html y personalmente encuentro el texto estándar de C ++ arriba perfectamente claro. Pero para resumir en palabras muy breves:
Si está en POSIX:
generic_category
=> POSIX standard errno space
system_category
=> Espacio de POSIX errno local (generalmente extiende POSIX con códigos de propiedad errno). Utilice strerror()
para expandir los códigos en descripciones de cadena devueltas por message()
.
En la práctica en POSIX, ambas implementaciones son las mismas debajo y mapean el espacio errno nativo.
Si en Windows:
generic_category
=> POSIX estándar errno espacio que es devuelto por varias funciones de emulación POSIX en el MSVCRT como fopen()
etc.
system_category
=> El GetLastError()
Win32 GetLastError()
. Use FormatMessage()
para expandir los códigos a descripciones de cadena devueltas por message()
.
Cómo usar <system_error> de forma portátil
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())
Otros pensamientos:
Algunos comentaristas han dicho que <system_error> está mal diseñado y no debería usarse. Esto simplemente no es cierto, es bastante óptimo dada la práctica idiomática de C ++ 03 en el momento de su diseño, genera un código de latencia fijo de alta calidad muy ajustado en todos los STL más importantes excepto en Dinkumware. Es extensible por el usuario a cualquier sistema de código de error arbitrario y estandariza la unificación en un único sistema de manejo de errores de biblioteca de terceros.
Es cierto que se vería muy diferente hoy en día si las variables globales constestadas estuvieran disponibles al momento de su diseño, y tal vez eso pueda ser rectificado en un estándar de C ++ después del 17. Pero si usted es un programador que necesita moverse de códigos de error de bibliotecas de terceros sin perder información a través del código no escrito para conocer esas bibliotecas de terceros, entonces <system_error> es una excelente solución.
Considere que es similar a la palabra clave virtual
para el manejo de código de error de la biblioteca de terceros: elimina la necesidad de que el código que transporta códigos de terceros tenga que entender esos códigos. Si tiene ese problema en su base de código, y la mayoría de las bases de códigos grandes lo hacen, entonces debe usar <system_error> en lugar del sistema de traducción de mapas de error o de error que esté utilizando actualmente.