Understanding the <system_error> facility in C++11

c++, c++11, error-code, error-handling, std-system-error

Solution

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`).

Problem

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: - Is my understanding about `std::error_code` and `std::error_condition` correct? - How I am supposed to know all the possible values of `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`? - What if we are on another platform and there is no way to figure out the exact system error code when an error occurs? - What if I want to have the same `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? - At the moment I am using a single `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?

Original source