2012-04-16 4 views
14

Ich möchte externe Bibliotheken (wie Boost) so weit wie möglich mit ihren Entsprechungen in Standard C++ ersetzen, wenn sie existieren und es ist möglich, Abhängigkeiten zu minimieren, daher frage ich mich, ob es eine sichere Möglichkeit gibt, boost::system::error_code zu std::error_code zu konvertieren. Pseudo-Code Beispiel:Ist es möglich, einen boost :: system :: error_code in einen std: error_code umzuwandeln?

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

Das wichtigste ist es nicht der genau der gleiche Fehler, nur so nahe wie möglich, und endlich, wenn ein Fehler ist oder nicht. Gibt es intelligente Lösungen?

Vielen Dank im Voraus!

+0

Möchten Sie die beiden gleichzeitig verwenden? Wenn nicht, sind die Schnittstellen nicht ähnlich genug, so dass ein einfaches "Suchen/Ersetzen" es tun würde? – ereOn

+1

Es ist nicht möglich. Sowohl std :: error_code als auch boost :: system :: error_code werden verwendet, aber ich habe es geschafft, boost :: system :: error_code für den Benutzer wegzuspulen, so dass es nie "sieht", also in Zukunft bei der letzten Abhängigkeit entfernt es so kann ich. – Fredrik

+0

Ich weiß nicht genug über beide API, um Ihnen den magic_code zu geben, aber ich kann sagen, dass der beste Weg, um dies vorwärts zu bewegen, wäre, eine '#ifdef USE_BOOST' in Kombination mit' typedef boost :: system :: error_code ErrorCodeType; 'und ein' # else' mit 'typedef std :: error_code ErrorCodeType;'. Dann können Sie progressive Änderungen an Ihrer Codebasis vornehmen, so dass beide mit den gleichen Schnittstellenaufrufen unterstützt werden. Wenn dann alles mit 'USE_BOOST' undefiniert arbeitet, können Sie den Schalter dauerhaft machen. Sonst wirst du am Ende an einem Seitenstrom arbeiten, der irgendwann vergessen wird. – Dennis

Antwort

8

Seit C++ - 11 (std :: errc) bildet boost/system/error_code.hpp die gleichen Fehlercodes auf std::errc ab, die in der Systemkopfzeile system_error definiert ist.

Sie können beide Enums vergleichen und sie sollten funktionell äquivalent sein, da beide auf dem POSIX-Standard basieren. Kann eine Besetzung erfordern.

Zum Beispiel

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

und 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

Danke, ich habe es mit dem folgenden Code zu arbeiten: std :: make_error_code (statisch_cast (err.value())) - es ist err eine instanz/referenz von boost :: system ::Fehlercode. – Fredrik

7

Ich hatte diese genau die gleiche Frage, da ich std::error_code verwenden wollte, wurde aber auch mit anderen Boost-Bibliotheken, die verwendet werden boost::system::error_code (zB Steigerung der ASIO) . Die angenommene Antwort funktioniert für die Fehlercodes, die von std::generic_category() behandelt werden, da es sich um eine einfache Umwandlung aus den allgemeinen Fehlercodes von Boost handelt, aber es funktioniert nicht für den allgemeinen Fall, in dem Sie auch benutzerdefinierte Fehlerkategorien behandeln möchten.

Also habe ich den folgenden Code als allgemeiner Zweck boost::system::error_code-zu-std::error_code Konverter erstellt. Es funktioniert, indem ein std::error_category Shim für jede boost::system::error_category dynamisch erzeugt wird, die Anrufe zur zugrundeliegenden Boost Fehlerkategorie weiterleitend. Da Fehlerkategorien Singletons sein müssen (oder zumindest Singleton-ähnlich wie in diesem Fall), erwarte ich nicht, dass es eine große Speicherexplosion gibt.

Ich auch nur konvertieren boost::system::generic_category() Objekt zu std::generic_category() verwenden, da sie sich gleich verhalten sollten. Ich wollte das gleiche für system_category() tun, aber beim Testen auf VC++ 10 es die falschen Nachrichten ausgedruckt (ich nehme an, es sollte ausdrucken, was Sie von FormatMessage erhalten, aber es scheint zu verwenden strerror, Boost verwendet FormatMessage wie erwartet) .

Um es zu verwenden rufen Sie einfach BoostToErrorCode(), unten definiert.

Nur eine Warnung, ich habe dies nur heute geschrieben, so dass es nur grundlegende Tests hatte. Sie können es beliebig verwenden, aber Sie tun dies auf eigenes Risiko.

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

Verwenden Sie diesen Code noch? Ist es nützlich genug, um vielleicht zur Steigerung beizutragen? – sehe

+0

@sehe AFAIK es ist immer noch in Gebrauch. Ich könnte sehen, dass es eine nützliche Ergänzung zu Boost ist, da konzeptionell die boost und std-Versionen von Fehlercodes genau dasselbe tun und nur aufgrund des Typsystems inkompatibel sind. In diesem Fall ist es wahrscheinlich besser direkt in den Boost-Fehlerklassen implementiert. Dies würde die Notwendigkeit für den Mutex und die Map beseitigen und würde die Konvertierung zu einem Preis machen, der auf Kosten von einigen weiteren Bytes pro Kategorie geht. Oder vielleicht könnte es direkt von der std abgeleitet werden, da Sie vielleicht auch von std-> boost gehen können? – Screndib

Verwandte Themen