Is it possible to convert a boost::system::error_code to a std:error_code?

12,879

Solution 1

Since C++-11 (std::errc), boost/system/error_code.hpp maps the same error codes to std::errc, which is defined in the system header system_error.

You can compare both enums and they should be functionally equivalent because they both appear to be based on the POSIX standard. May require a cast.

For example,

namespace posix_error
    {
      enum posix_errno
      {
        success = 0,
        address_family_not_supported = EAFNOSUPPORT,
        address_in_use = EADDRINUSE,
        address_not_available = EADDRNOTAVAIL,
        already_connected = EISCONN,
        argument_list_too_long = E2BIG,
        argument_out_of_domain = EDOM,
        bad_address = EFAULT,
        bad_file_descriptor = EBADF,
        bad_message = EBADMSG,
        ....
       }
     }

and std::errc

address_family_not_supported  error condition corresponding to POSIX code EAFNOSUPPORT  

address_in_use  error condition corresponding to POSIX code EADDRINUSE  

address_not_available  error condition corresponding to POSIX code EADDRNOTAVAIL  

already_connected  error condition corresponding to POSIX code EISCONN  

argument_list_too_long  error condition corresponding to POSIX code E2BIG  

argument_out_of_domain  error condition corresponding to POSIX code EDOM  

bad_address  error condition corresponding to POSIX code EFAULT 

Solution 2

I had this exact same question since I wanted to use std::error_code but was also using other boost libraries that use boost::system::error_code (e.g. boost ASIO). The accepted answer works for the error codes handled by std::generic_category(), as they're a simple cast from boost's generic error codes, but it doesn't work for the general case where you want to handle custom error categories as well.

So I created the following code as a general purpose boost::system::error_code-to-std::error_code converter. It works by dynamically creating an std::error_category shim for each boost::system::error_category, forwarding the calls to the underlying Boost error category. Since error categories need to be singletons (or at least singleton-like as in this case) I don't expect there to be much of a memory explosion.

I also just convert boost::system::generic_category() object to use std::generic_category() since they should behave the same. I had wanted to do the same for system_category(), however in testing on VC++10 it printed out the wrong messages (I assume it should print out what you get from FormatMessage, but it appears to use strerror, Boost uses FormatMessage as expected).

To use it just call BoostToErrorCode(), defined below.

Just a warning, I just wrote this today so it's only had basic testing. You may use it any way you like, but you do so at your own risk.

//==================================================================================================
// These classes implement a shim for converting a boost::system::error_code to a std::error_code.
// Unfortunately this isn't straightforward since it the error_code classes use a number of
// incompatible singletons.
//
// To accomplish this we dynamically create a shim for every boost error category that passes
// the std::error_category calls on to the appropriate boost::system::error_category calls.
//==================================================================================================
#include <boost/system/error_code.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/once.hpp>
#include <boost/thread/locks.hpp>

#include <system_error>
namespace
{
    // This class passes the std::error_category functions through to the
    // boost::system::error_category object.
    class BoostErrorCategoryShim : public std::error_category
    {
    public:
        BoostErrorCategoryShim( const boost::system::error_category& in_boostErrorCategory )
            :m_boostErrorCategory(in_boostErrorCategory), m_name(std::string("boost.") + in_boostErrorCategory.name()) {}

        virtual const char *name() const;
        virtual std::string message(value_type in_errorValue) const;
        virtual std::error_condition default_error_condition(value_type in_errorValue) const;

    private:
        // The target boost error category.
        const boost::system::error_category& m_boostErrorCategory;

        // The modified name of the error category.
        const std::string m_name;
    };

    // A converter class that maintains a mapping between a boost::system::error_category and a
    // std::error_category.
    class BoostErrorCodeConverter
    {
    public:
        const std::error_category& GetErrorCategory( const boost::system::error_category& in_boostErrorCategory )
        {
            boost::lock_guard<boost::mutex> lock(m_mutex);

            // Check if we already have an entry for this error category, if so we return it directly.
            ConversionMapType::iterator stdErrorCategoryIt = m_conversionMap.find(&in_boostErrorCategory);
            if( stdErrorCategoryIt != m_conversionMap.end() )
                return *stdErrorCategoryIt->second;

            // We don't have an entry for this error category, create one and add it to the map.                
            const std::pair<ConversionMapType::iterator, bool> insertResult = m_conversionMap.insert(
                ConversionMapType::value_type(
                    &in_boostErrorCategory, 
                    std::unique_ptr<const BoostErrorCategoryShim>(new BoostErrorCategoryShim(in_boostErrorCategory))) );

            // Return the newly created category.
            return *insertResult.first->second;
        }

    private:
        // We keep a mapping of boost::system::error_category to our error category shims.  The
        // error categories are implemented as singletons so there should be relatively few of
        // these.
        typedef std::unordered_map<const boost::system::error_category*, std::unique_ptr<const BoostErrorCategoryShim>> ConversionMapType;
        ConversionMapType m_conversionMap;

        // This is accessed globally so we must manage access.
        boost::mutex m_mutex;
    };


    namespace Private
    {
        // The init flag.
        boost::once_flag g_onceFlag = BOOST_ONCE_INIT;

        // The pointer to the converter, set in CreateOnce.
        BoostErrorCodeConverter* g_converter = nullptr;

        // Create the log target manager.
        void CreateBoostErrorCodeConverterOnce()
        {
            static BoostErrorCodeConverter converter;
            g_converter = &converter;
        }
    }

    // Get the log target manager.
    BoostErrorCodeConverter& GetBoostErrorCodeConverter()
    {
        boost::call_once( Private::g_onceFlag, &Private::CreateBoostErrorCodeConverterOnce );

        return *Private::g_converter;
    }

    const std::error_category& GetConvertedErrorCategory( const boost::system::error_category& in_errorCategory )
    {
        // If we're accessing boost::system::generic_category() or boost::system::system_category()
        // then just convert to the std::error_code versions.
        if( in_errorCategory == boost::system::generic_category() )
            return std::generic_category();

        // I thought this should work, but at least in VC++10 std::error_category interprets the
        // errors as generic instead of system errors.  This means an error returned by
        // GetLastError() like 5 (access denied) gets interpreted incorrectly as IO error.
        //if( in_errorCategory == boost::system::system_category() )
        //  return std::system_category();

        // The error_category was not one of the standard boost error categories, use a converter.
        return GetBoostErrorCodeConverter().GetErrorCategory(in_errorCategory);
    }


    // BoostErrorCategoryShim implementation.
    const char* BoostErrorCategoryShim::name() const
    {
        return m_name.c_str();
    }

    std::string BoostErrorCategoryShim::message(value_type in_errorValue) const
    {
        return m_boostErrorCategory.message(in_errorValue);
    }

    std::error_condition BoostErrorCategoryShim::default_error_condition(value_type in_errorValue) const
    {
        const boost::system::error_condition boostErrorCondition = m_boostErrorCategory.default_error_condition(in_errorValue);

        // We have to convert the error category here since it may not have the same category as
        // in_errorValue.
        return std::error_condition( boostErrorCondition.value(), GetConvertedErrorCategory(boostErrorCondition.category()) );
    }
}

std::error_code BoostToErrorCode( boost::system::error_code in_errorCode )
{
    return std::error_code( in_errorCode.value(), GetConvertedErrorCategory(in_errorCode.category()) );
}

Solution 3

I adapted the above solution into a shorter solution. Using a thread_local std::map to map between the category name to its instance so no locking is needed.

The limitation is that you can't pass error codes between threads since the category pointer will be different.(Converting it to a locking function is simple enough if you don't want to use the thread_local storage)

Also I feed it is more compact.

#include <iostream>
#include <map>
#include <boost/system/system_error.hpp>

namespace std
{

error_code make_error_code(boost::system::error_code error)
{
    struct CategoryAdapter : public error_category
    {
        CategoryAdapter(const boost::system::error_category& category)
            : m_category(category)
        {
        }

        const char* name() const noexcept
        {
            return m_category.name();
        }

        std::string message(int ev) const
        {
            return m_category.message(ev);
        }

    private:
        const boost::system::error_category& m_category;
    };

    static thread_local map<std::string, CategoryAdapter> nameToCategory;
    auto result = nameToCategory.emplace(error.category().name(), error.category());
    auto& category = result.first->second;
    return error_code(error.value(), category);
}

};

int main() {
    auto a = boost::system::errc::make_error_code(boost::system::errc::address_family_not_supported);
    auto b = std::make_error_code(a);
    std::cout << b.message() << std::endl;
}
Share:
12,879

Related videos on Youtube

Fredrik
Author by

Fredrik

Updated on June 06, 2022

Comments

  • Fredrik
    Fredrik almost 2 years

    I want to replace external libraries (like boost) as much as possible with their equivalents in standard C++ if they exist and it is possible, to minimize dependencies, therefore I wonder if there exists a safe way to convert boost::system::error_code to std::error_code. Pseudo code example:

    void func(const std::error_code & err)
    {
        if(err) {
            //error
        } else {
            //success
        }
    }
    
    boost::system::error_code boost_err = foo(); //foo() returns a boost::system::error_code
    std::error_code std_err = magic_code_here; //convert boost_err to std::error_code here
    func(std_err);
    

    The most important it is not the exactly the same error, just so close to as possible and at last if is an error or not. Are there any smart solutions?

    Thanks in advance!

    • ereOn
      ereOn about 12 years
      Do you want to use the two simultaneously ? If not, aren't the interfaces are similar enough so that a simple "search/replace" would do it ?
    • Fredrik
      Fredrik about 12 years
      It is not possible. Both std::error_code and boost::system::error_code are used, but I have manage to abstract away boost::system::error_code for the user, so it never "sees it", so in the future when the last dependency removes it so can I.
    • Dennis
      Dennis about 12 years
      I don't know enough about either API to give you the magic_code, but I can say that the best way to progressively move this forward would be to use a #ifdef USE_BOOST in combination with typedef boost::system::error_code ErrorCodeType; and an #else with typedef std::error_code ErrorCodeType;. Then you can make progressive changes to your code base so that both are supported using the same interface calls, and then when it's all working with USE_BOOST undefined you can make the switch permanent. Otherwise you'll just end up working on a side stream that will be eventually forgotten.
  • Fredrik
    Fredrik about 12 years
    Thanks, I got it to work by using the following code: std::make_error_code(static_cast<std::errc::errc>(err.value(‌​))) - there err is an instance/reference of boost::system::error_code.
  • sehe
    sehe over 9 years
    Do you still use this code? Is it useful enough to perhaps contribute to boost?
  • joey
    joey over 9 years
    @sehe AFAIK it's still in use. I could see it being a useful addition to boost since, conceptually, the boost and std versions of error codes do the exact same thing and are only incompatible due to the type system. In that case though it's probably better implemented directly in the boost error category classes. That would remove the need for the mutex and map and would make the conversion noexcept, at the expense of a few more bytes per category. Or perhaps it could just derive from the std directly since you might also want to be able to go from std->boost?