Вопрос по std, c++, boost, error-code – Можно ли преобразовать boost :: system :: error_code в std: error_code?

15

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

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

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

Заранее спасибо!

Это невозможно. Используются как std :: error_code, так и boost :: system :: error_code, но мне удалось абстрагироваться от boost :: system :: error_code для пользователя, поэтому он никогда не "видит его", поэтому в будущем, когда последний зависимость удаляет это, я тоже Fredrik
Вы хотите использовать два одновременно? Если нет, то интерфейсы не достаточно похожи, так что простой поиск / замена & quot; сделал бы это? ereOn
Я не знаю достаточно ни об одном API, чтобы дать вам magic_code, но могу сказать, что лучший способ для постепенного продвижения вперед - это использовать#ifdef USE_BOOST в комбинации сtypedef boost::system::error_code ErrorCodeType; и#else сtypedef std::error_code ErrorCodeType;, Затем вы можете вносить последовательные изменения в свою кодовую базу, чтобы оба они поддерживали, используя одни и те же вызовы интерфейса, а затем, когда все это работает сUSE_BOOST undefined вы можете сделать переключатель постоянным. В противном случае вы просто будете работать над побочным потоком, который в конечном итоге будет забыт. Dennis

Ваш Ответ

3   ответа
0

Я адаптировал вышеупомянутое решение в более короткое решение. Использование thread_local std :: map для сопоставления между именем категории и ее экземпляром, поэтому блокировка не требуется.

Ограничение состоит в том, что вы не можете передавать коды ошибок между потоками, поскольку указатель категории будет другим (преобразование его в функцию блокировки достаточно просто, если вы не хотите использовать хранилище thread_local)

Также я кормлю это более компактно.

#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;
}
8

Начиная с C ++ - 11 (std :: errc),повышение / система / error_code.hpp сопоставляет те же коды ошибок сстанд :: ЕЦПР, который определяется в системном заголовке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 
Спасибо, я заставил его работать, используя следующий код: std :: make_error_code (static_cast & lt; std :: errc :: errc & gt; (err.value ())) - там err есть экземпляр / ссылка на boost :: system: :код ошибки. Fredrik
8

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

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

Я также просто конвертироватьboost::system::generic_category() объект для использованияstd::generic_category() так как они должны вести себя одинаково. Я хотел сделать то же самое дляsystem_category()Однако при тестировании на VC ++ 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()) );
}
Вы все еще используете этот код? Это достаточно полезно, чтобы, возможно, способствовать повышению?
@sehe AFAIK он все еще используется. Я мог бы видеть, что это полезное дополнение для boost, поскольку концептуально, boost и std версии кодов ошибок делают одно и то же и несовместимы только из-за системы типов. В этом случае, однако, это, вероятно, лучше реализовать непосредственно в классах категории ошибок повышения. Это устранит необходимость в мьютексе и карте и сделает преобразование без исключения, за счет еще нескольких байтов на категорию. Или, возможно, он может быть получен непосредственно из std, поскольку вы также можете захотеть перейти из std-> boost?

Похожие вопросы