Можно ли преобразовать boost:: system:: error_code в std: error_code?

Я хочу как можно больше заменить внешние библиотеки (например, boost) своими эквивалентами в стандартном С++, если они существуют, и возможно, минимизировать зависимости, поэтому я задаюсь вопросом, существует ли безопасный способ преобразования boost::system::error_code в std::error_code. Пример псевдокода:

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

Самое главное - это не точно такая же ошибка, как можно ближе и, наконец, если это ошибка или нет. Есть ли какие-нибудь умные решения?

Спасибо заранее!

Ответы

Ответ 1

Так как С++ - 11 (std:: errc), boost/system/error_code.hpp сопоставляет те же коды ошибок с std:: errc, который определен в системном заголовке system_error.

Вы можете сравнить оба перечисления, и они должны быть функционально эквивалентными, поскольку оба они, как представляется, основаны на стандарте POSIX. Может потребоваться бросок.

Например,

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,
        ....
       }
     }

и 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 

Ответ 2

У меня был такой же вопрос, поскольку я хотел использовать std::error_code, но также использовал другие библиотеки boost, которые используют boost::system::error_code (например, boost ASIO). Принятый ответ работает для кодов ошибок, обрабатываемых std::generic_category(), поскольку они представляют собой простой набор из общих генерирующих кодов ошибок, но он не работает для общего случая, когда вы также должны обрабатывать собственные категории ошибок.

Итак, я создал следующий код как конвертер общего назначения boost::system::error_code -to- std::error_code. Он работает путем динамического создания прокладки std::error_category для каждого boost::system::error_category, пересылки вызовов в базовую категорию ошибок Boost. Поскольку категории ошибок должны быть одноточечными (или, по крайней мере, одноточечными, как в этом случае), я не ожидаю, что там будет большой взрыв памяти.

Я также просто конвертирую объект boost::system::generic_category() для использования std::generic_category(), так как они должны вести себя одинаково. Я хотел сделать то же самое для system_category(), однако при тестировании на VС++ 10 он напечатал неправильные сообщения (я предполагаю, что он должен распечатать то, что вы получаете от FormatMessage, но, похоже, использует strerror, Boost использует FormatMessage, как ожидалось).

Чтобы использовать его, просто вызовите BoostToErrorCode(), определенный ниже.

Просто предупреждение, я только что написал это сегодня, поэтому у него было только базовое тестирование. Вы можете использовать его так, как вам нравится, но вы делаете это на свой страх и риск.

//==================================================================================================
// 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()) );
}