2012-04-16 19 views
14

Quiero reemplazar las bibliotecas externas (como boost) tanto como sea posible con sus equivalentes en C++ estándar si existen y es posible, para minimizar las dependencias, por lo tanto, me pregunto si existe una forma segura de convertir boost::system::error_code en std::error_code. Pseudo código de ejemplo:¿Es posible convertir un boost :: system :: error_code en 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); 

El más importante no es el exactamente el mismo error, tan cerca como sea posible y, por fin, si es un error o no. ¿Hay alguna solución inteligente?

¡Gracias de antemano!

+0

¿Desea utilizar los dos al mismo tiempo? Si no, ¿no son las interfaces lo suficientemente similares como para que una simple "búsqueda/reemplazo" lo haga? – ereOn

+1

No es posible. Se usan std :: error_code y boost :: system :: error_code, pero he logrado abstraer el boost :: system :: error_code para el usuario, por lo que nunca lo "ve", por lo que en el futuro cuando la última dependencia lo quita también yo. – Fredrik

+0

No sé lo suficiente acerca de cualquiera de las API para darle el código_mágico, pero puedo decir que la mejor forma de avanzar progresivamente sería usar un '#ifdef USE_BOOST' en combinación con' typedef boost :: system :: error_code ErrorCodeType; 'y un' # else' con 'typedef std :: error_code ErrorCodeType;'. Luego puede hacer cambios progresivos en su base de códigos para que ambos sean compatibles usando las mismas llamadas de interfaz, y luego, cuando todo esté funcionando con 'USE_BOOST 'indefinido, puede hacer que el conmutador sea permanente. De lo contrario, terminarás trabajando en un flujo lateral que finalmente se olvidará. – Dennis

Respuesta

8

Dado que C++ - 11 (std :: errc), boost/system/error_code.hpp asigna los mismos códigos de error a std::errc, que se define en el encabezado del sistema system_error.

Puede comparar ambas enumeraciones y deben ser funcionalmente equivalentes, ya que ambas parecen estar basadas en el estándar POSIX. Puede requerir un yeso.

Por ejemplo,

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

y 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

Gracias, lo hice funcionar usando el siguiente código: std :: make_error_code (static_cast (err.value())) - err es una instancia/referencia de boost :: system ::código de error. – Fredrik

7

Tenía esta exactamente la misma cuestión, ya que quería utilizar std::error_code sino que también estaba usando otras bibliotecas Boost que utilizan boost::system::error_code (por ejemplo, aumentar ASIO) . La respuesta aceptada funciona para los códigos de error manejados por std::generic_category(), ya que son un simple molde de los códigos de error genéricos de boost, pero no funciona para el caso general en el que también desee manejar categorías de error personalizadas.

Así que creé el siguiente código como un convertidor de uso general boost::system::error_code -to- std::error_code. Funciona creando dinámicamente un ajuste std::error_category para cada boost::system::error_category, reenviando las llamadas a la categoría de error de Boost subyacente. Como las categorías de error deben ser simples (o al menos como las de un singleton como en este caso), no espero que haya una gran explosión de memoria.

También acabo de convertir boost::system::generic_category() objeto para usar std::generic_category() ya que deberían comportarse igual. Yo quería hacer lo mismo para system_category(), pero al probar en VC++ 10 imprimió los mensajes incorrectos (supongo que debería imprimir lo que obtienes de FormatMessage, pero parece que usa strerror, Boost usa FormatMessage como se esperaba) .

Para usarlo simplemente llame al BoostToErrorCode(), se define a continuación.

Solo una advertencia, acabo de escribir esto hoy, así que solo ha tenido pruebas básicas. Puede usarlo como quiera, pero lo hace bajo su propio riesgo.

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

¿Todavía usa este código? ¿Es lo suficientemente útil como para contribuir a impulsar? – sehe

+0

@sehe AFAIK todavía está en uso. Pude ver que es una adición útil al impulso ya que, conceptualmente, las versiones de impulso y estándar de los códigos de error hacen exactamente lo mismo y solo son incompatibles debido al sistema de tipo. En ese caso, aunque probablemente sea mejor implementado directamente en las clases de categoría de error de impulso. Eso eliminaría la necesidad del mutex y el mapa y haría la conversión no salvo, a expensas de unos pocos bytes más por categoría. ¿O tal vez podría derivar directamente del estándar, ya que también podría querer pasar de std-> boost? – Screndib

Cuestiones relacionadas