Entendiendo la facilidad<system_error> en C++ 11
c++11 error-handling (1)
La principal distinción es que std::error_condition
es portátil (independiente de la plataforma) mientras que std::error_code
depende de la plataforma. Normalmente, el código dependiente de la plataforma de bajo nivel genera códigos de error_codes
y el código del cliente compara estos códigos de error_codes
con las condiciones de error_conditions
independientes de la plataforma.
19.5 [syserr] define una larga lista de condiciones de error estándar (y portátiles) (por ejemplo, errc::no_such_file_or_directory
) que están explícitamente asociadas a valores específicos de errno
(por ejemplo, ENOENT
). Como resultado, no necesita conocer la lista completa de valores posibles de errno
o GetLastError()
generados en su sistema. Solo necesita conocer los valores estándar relevantes para su código. Por ejemplo, la implementación de su biblioteca podría verse así:
void MyLibraryClass::foo(std::error_code &ec)
{
// whatever platform dependent operation that might set errno
// possibly with alternative platform-dependent implementations
ec = make_error_code(errno);
}
Su código de cliente luego verificará si el error_code
coincide con alguna condición de error_condition
específica:
error_code ec;
myLibraryInstance.foo(ec);
if (!ec)
{
// success
}
else if (errc::no_such_file_or_directory == ec)
{
// no_such_file_or_directory
}
else
{
// unknown or unexpected error
}
En su caso, probablemente definiría su propia enumeración de errores (solo una enumeración) y la etiquetaría como error_conditions
para habilitar la conversión automática:
namespace commons
{
namespace dynlib
{
enum class errc {LibraryFailedToLoad=1, LibraryFailedToUnload, SymbolNotFound};
}
}
namespace std
{
template<> struct is_error_condition_enum<commons::dynlib::errc> : true_type {};
}
// TODO: implement make_error_code and make_error_condition
A continuación, podría traducir el resultado de las diversas operaciones dependientes de la plataforma en la error_condition
(o error_code
si lo prefiere):
void DynLibLoader::open(std::error_code &ec)
{
// possibly implement the windows version here as well
if (NULL == dlopen(filename, flag))
{
ec = make_error_code(errc::LibraryFailedToLoad);
}
}
Su código de cliente compararía el código de error con las posibles condiciones de error, como se indica anteriormente:
error_code ec;
dynLibLoader.open(ec);
if (!ec)
{
// success
}
else if (commons::dynlib::errc::LibraryFailedToLoad == ec)
{
// Library Failed To Load
}
else
{
// unknown or unexpected error
}
Tenga en cuenta que la enumeración commons::dynlib::errc::LibraryFailedToLoad
se convierte automáticamente en un error_condition
(utilizando el método make_error_condition
proporcionado) porque commons::dynlib::errc
está etiquetado con is_error_condition_enum
.
La asignación de error_category
al espacio de nombres es probablemente una preferencia personal, sin embargo, parece un poco artificial. En este caso específico, es cierto que tiene sentido tener una categoría para el espacio de nombres de dynlib
pero sería fácil encontrar ejemplos en los que tendría sentido tener categorías que difundan varios espacios de nombres. En algunos casos, podría ser significativo y práctico tener todas sus enumeraciones de error diferentes en un espacio de nombres único (por ejemplo, commons::errors
).
Estoy tratando de usar la instalación system_error
para manejar los errores en una biblioteca mía. Voy a discutir brevemente la estructura de la biblioteca en caso de que le resulte útil: el espacio de nombres de la biblioteca se llama commons
y debajo de este tengo otro espacio de nombres llamado dynlib
. dynlib
contiene clases que son responsables de cargar archivos .so / .dll:
namespace commons {
namespace dynlib {
class DynLibLoader {
};
}
}
Los errores que pueden producirse en DynLibLoader son LibraryFailedToLoad
, LibraryFailedToUnload
y SymbolNotFound
. Así que mis pensamientos para manejar los errores son los siguientes: Agregaré un error
espacio de nombres debajo del espacio de nombres dynlib
. Luego, bajo ese espacio de nombres definiré una enumeración para std::error_codes
y una enumeración para std::error_conditions
. A mi entender, los std::error_codes
deben corresponder al valor de errno
(Linux) o GetLastError
(Win32), y las condiciones std::error_conditions
a valores como LibraryFailedToLoad
, SymbolNotFound
, etc. Entonces, aquí están mis preguntas:
- ¿Mi entendimiento sobre
std::error_code
ystd::error_condition
correcto? - ¿Cómo se supone que debo conocer todos los valores posibles de
errno
yGetLastError()
para definirlos en mi enumstd::error_codes
? ¿Qué sucede si Microsoft agrega valores de error adicionales a la API en el futuro? ¿Tendré que volver al código fuente y definirlos bajo la enumeración que tengo para los códigosstd::error_codes
? - ¿Qué sucede si estamos en otra plataforma y no hay manera de averiguar el código de error exacto del sistema cuando se produce un error?
- ¿Qué
dynlib
si quiero tener los mismosstd::error_codes
para todo el espacio de nombres de commons y solo defino unastd::error_condition
diferentestd::error_condition
para cada sub espacio de nombres comodynlib
? ¿Es esta una buena practica? Yo diría que sí porque esto evitará el código duplicado. ¿Pero hay una trampa detrás de esto? - En este momento estoy usando una sola
std::error_category
para cada sub-espacio de nombres de commons. ¿Es esta una buena practica? ¿Crees que debería usarstd::error_category
diferente?