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