2012-04-16 2 views
14

Я хочу как можно больше заменить внешние библиотеки (например, boost) их эквивалентами в стандартном C++, если они существуют, и это возможно, чтобы минимизировать зависимости, поэтому я задаюсь вопросом, существует ли безопасный способ конвертировать boost::system::error_code в std::error_code. Псевдокод пример:Можно ли преобразовать 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); 

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

Заранее благодарен!

+0

Вы хотите использовать оба одновременно? Если нет, не являются ли интерфейсы достаточно похожими, чтобы простой «поиск/замена» сделал бы это? – ereOn

+1

Невозможно. Используются как std :: error_code, так и boost :: system :: error_code, но мне удалось абстрагироваться от boost :: system :: error_code для пользователя, поэтому он никогда «не видит», поэтому в будущем, когда последняя зависимость удаляет его, так что я могу. – Fredrik

+0

Я не знаю достаточно об API, чтобы дать вам magic_code, но могу сказать, что лучшим способом постепенного продвижения вперед было бы использовать '#ifdef USE_BOOST' в сочетании с' typedef boost :: system :: error_code ErrorCodeType; 'и' # else' с 'typedef std :: error_code ErrorCodeType;'. Затем вы можете внести прогрессивные изменения в свою базу кода, чтобы оба они поддерживались с использованием одних и тех же вызовов интерфейса, а затем, когда все они работают с 'USE_BOOST', вы не можете установить постоянный переключатель. В противном случае вы просто закончите работу над боковым потоком, который в конце концов будет забыт. – Dennis

ответ

8

Поскольку C++ - 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 
+3

Спасибо, я получил его для работы, используя следующий код: std :: make_error_code (static_cast (err.value())) - там err - это экземпляр/ссылка boost :: system ::код ошибки. – Fredrik

7

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

Таким образом, я создал следующий код в качестве общего назначения 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(), однако при тестировании на 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())); 
} 
+0

Вы все еще используете этот код? Является ли это достаточно полезным, чтобы, возможно, способствовать росту? – sehe

+0

@sehe AFAIK все еще используется. Я мог видеть, что это полезное дополнение к увеличению, поскольку, концептуально, коды ошибок boost и std делают то же самое и являются только несовместимыми из-за системы типов. В этом случае, вероятно, это лучше всего реализовать непосредственно в классах категорий ошибок повышения. Это устранило бы необходимость в мьютексе и карте и сделало бы преобразование noexcept, за счет нескольких байт на каждую категорию. Или, может быть, это может быть просто из std напрямую, так как вы также можете захотеть перейти от std-> boost? – Screndib