Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding the <system_error> facility in C++11

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?
like image 525
AstrOne Avatar asked Feb 17 '14 05:02

AstrOne


1 Answers

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

like image 177
Come Raczy Avatar answered Oct 06 '22 00:10

Come Raczy