2012-04-16 17 views
14

Chcę zastąpić biblioteki zewnętrzne (jak boost) w jak największym stopniu z ich odpowiednikami w standardowym C++, jeśli istnieją i jest to możliwe, aby zminimalizować zależności, dlatego zastanawiam się, czy istnieje bezpieczny sposób konwersji boost::system::error_code na std::error_code. Przykładowy kod pseudo:Czy można przekonwertować boost :: system :: error_code na 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); 

Najważniejszy nie jest to dokładnie ten sam błąd, tak blisko jak to możliwe, w końcu jeśli jest to błąd czy nie. Czy są jakieś inteligentne rozwiązania?

Z góry dziękuję!

+0

Czy chcesz użyć dwóch jednocześnie? Jeśli nie, to czy interfejsy nie są do siebie wystarczająco podobne, aby wystarczyło to do "wyszukiwania/zamiany"? – ereOn

+1

Nie jest to możliwe. Oba std :: error_code i boost :: system :: error_code są używane, ale udało mi się usunąć streszczenie boost :: system :: error_code dla użytkownika, więc nigdy "go nie widzi", więc w przyszłości, gdy ostatnia zależność usuwa to, tak ja mogę I. – Fredrik

+0

Nie wiem wystarczająco dużo na temat któregoś z interfejsów API, aby podać kod magic_code, ale mogę powiedzieć, że najlepszym sposobem stopniowego przenoszenia tego rozszerzenia byłoby użycie '#ifdef USE_BOOST' w połączeniu z' typedef boost :: system :: error_code ErrorCodeType; 'i' # else' z 'typedef std :: error_code ErrorCodeType;'. Następnie możesz wprowadzać progresywne zmiany w bazie kodu, dzięki czemu oba będą obsługiwane za pomocą tych samych wywołań interfejsu, a gdy wszystko będzie działać z niezdefiniowanym 'USE_BOOST', możesz uczynić przełącznik stałym. W przeciwnym razie skończysz pracować nad strumieniem bocznym, który zostanie w końcu zapomniany. – Dennis

Odpowiedz

8

Ponieważ C++ - 11 (std :: errc), boost/system/error_code.hpp odwzorowuje te same kody błędów na std::errc, co jest zdefiniowane w nagłówku systemu system_error.

Można porównać oba wyliczenia i powinny one być funkcjonalnie równoważne, ponieważ oba wydają się być oparte na standardzie POSIX. Może wymagać obsady.

Na przykład

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

i 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

Dzięki, mam do pracy przy użyciu następującego kodu: std :: make_error_code (static_cast (err.value())) - tam err jest instancją/referencją systemu boost :: system ::Kod błędu. – Fredrik

7

miałem dokładnie ten sam pytanie ponieważ chciałem użyć std::error_code ale także za pomocą innych bibliotek Boost, które używają boost::system::error_code (np zwiększyć ASIO) . Zaakceptowana odpowiedź działa dla kodów błędów obsługiwanych przez std::generic_category(), ponieważ są one prostym rzutem z ogólnych kodów błędów boost, ale nie działa to w ogólnym przypadku, w którym chcesz również obsłużyć niestandardowe kategorie błędów.

Więc stworzyłem następujący kod jako uniwersalny konwerter boost::system::error_code-na-std::error_code. Działa dynamicznie tworząc podkładkę std::error_category dla każdego boost::system::error_category, przekazując wywołania do podstawowej kategorii błędów Boost. Ponieważ kategorie błędów muszą być pojedynczymi (lub przynajmniej podobnymi do singletonów, jak w tym przypadku), nie spodziewam się dużej eksplozji pamięci.

Również po prostu konwertuję obiekt boost::system::generic_category() do użycia std::generic_category(), ponieważ powinny one zachowywać się tak samo. Chciałem zrobić to samo dla system_category(), jednak w testowaniu na VC++ 10 wydrukowano złe komunikaty (zakładam, że powinien wydrukować to, co dostałeś z FormatMessage, ale wydaje się, że używasz strerror, Boost używa FormatMessage zgodnie z oczekiwaniami) .

Aby z niego skorzystać wystarczy zadzwonić pod numer BoostToErrorCode(), zdefiniowany poniżej.

Właśnie ostrzeżenie, właśnie napisałem to dzisiaj, więc to tylko miało podstawowe testowanie. Możesz go używać w dowolny sposób, ale robisz to na własne ryzyko.

//================================================================================================== 
// 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

Czy nadal używasz tego kodu? Czy jest to wystarczająco przydatne, aby przyczynić się do zwiększenia? – sehe

+0

@sehe AFAIK jest nadal w użyciu. Mogłem zobaczyć, że jest to przydatny dodatek do zwiększenia, ponieważ, koncepcyjnie, boost i std wersje kodów błędów robią dokładnie to samo i są tylko niekompatybilne ze względu na system typów. W takim przypadku prawdopodobnie lepiej jest zaimplementować bezpośrednio w klasach błędów doładowania. To usunie potrzebę muteksa i mapowania i spowoduje, że konwersja nie potrwa, kosztem kilku dodatkowych bajtów na kategorię. A może może po prostu czerpać z STD bezpośrednio, ponieważ możesz również chcieć przejść od std-> boost? – Screndib

Powiązane problemy