I am trying to use the system_error
facility to handle errors in a library of mine. I am going to briefly discuss the structure of the library in case you find it helpful: The namespace of the library is called commons
and under this I have another namespace called dynlib
. dynlib
contains classes that are responsible for loading .so/.dll files:
namespace commons {
namespace dynlib {
class DynLibLoader {
};
}
}
The errors that may occur in the DynLibLoader are LibraryFailedToLoad
, LibraryFailedToUnload
and SymbolNotFound
. So my thoughts for handling the errors are the following: I will add a namespace error
under the namespace dynlib
. Then, under that namespace I will define one enum for std::error_codes
and one enum for std::error_conditions
. From my understanding the std::error_codes
have to correspond to the value of errno
(Linux) or GetLastError
(Win32), and the std::error_conditions
to values like LibraryFailedToLoad
, SymbolNotFound
etc. So, here are my questions:
std::error_code
and std::error_condition
correct?errno
and GetLastError()
in order to define them under my std::error_codes
enum? What if Microsoft adds extra error values to the API in the future? Will I have to go back to the source code and define them under the enum I have for the std::error_codes
?std::error_codes
for the entire commons namespace and only define a different std::error_condition
for each sub-namespace like dynlib
. Is this a good practice? I would say yes because this will avoid duplicate code. But is there a catch behind this?std::error_category
for each sub-namespace of commons. Is this a good practice? Do you think I should use the std::error_category
differently?The main distinction is that std::error_condition
is portable (platform-independent) while std::error_code
is platform-dependent. Typically, low-level platform-dependent code generates error_codes
and the client code compares these error_codes
to platform-independent error_conditions
.
19.5 [syserr] defines a long list of standard (and portable) error conditions (e.g. errc::no_such_file_or_directory
) that are explicitly associated to specific values of errno
(e.g. ENOENT
). As a result, you do not need to know the complete list of possible values of errno
or GetLastError()
generated on your system. You only need to know the standard values relevant to your code. For instance, your library implementation could look like this:
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);
}
Your client code would then check if the error_code
matches any specific error_condition
:
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
}
In your case, you would probably define your own enumeration of errors (only one enumeration) and tag it as error_conditions
to enable automatic conversion:
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
You could then translate the outcome of the various platform-dependent operations into the appropriate error_condition
(or error_code
if you prefer):
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);
}
}
Your client code would compare the error code to the possible error conditions, as above:
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
}
Note that the enum commons::dynlib::errc::LibraryFailedToLoad
is automatically converted to an error_condition
(using the provided make_error_condition
method) because commons::dynlib::errc
is tagged with is_error_condition_enum
.
The mapping of error_category
to the namespace is probably a personal preference, however it seems a bit artificial. In this specific case, it is true that it makes sense to have a category for the dynlib
namespace but it would be easy to find examples where it would make sense to have categories spreading several namespaces. In some cases it might be meaningful and practical to have all your different error enums in a unique namespace (e.g. commons::errors
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With