tipico simbolo regresion promedio lineal interpretacion estimacion estandar error ejemplo c++ c++11 winapi posix system-error

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:

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 miembro category() ese objeto devolverá std::system_category() para los errores que se originan en el sistema operativo, o una referencia a un objeto error_category definido por la error_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 de std::system_category() como idénticos a los valores de POSIX errno , 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 / POSIX EFOOBAR ;

    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 un erro_code usando generic_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 forma generic_category para los valores POSIX de errno ;

  • se supone que no se puede usar std::generic_category para valles no POSIX de errno (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 usar system_error con errno (estrictamente hablando, el soporte para esto no es obligatorio, solo es system_error ). en sistemas basados ​​en POSIX puede usar system_error con errno .


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.