是否可以将 boost::system::error_code 转换为 std:error_code?
Posted
技术标签:
【中文标题】是否可以将 boost::system::error_code 转换为 std:error_code?【英文标题】:Is it possible to convert a boost::system::error_code to a std:error_code? 【发布时间】:2012-04-27 22:06:50 【问题描述】:我想尽可能地用标准 C++ 中的等价物替换外部库(如 boost),如果它们存在并且可能的话,以尽量减少依赖关系,因此我想知道是否存在将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);
最重要的不是完全相同的错误,只是尽可能接近,最后是否是错误。有什么聪明的解决方案吗?
提前致谢!
【问题讨论】:
你想同时使用这两个吗?如果不是,界面是否足够相似,以至于一个简单的“搜索/替换”就可以做到? 不可能。 std::error_code 和 boost::system::error_code 都被使用了,但我已经设法为用户抽象出 boost::system::error_code ,所以它永远不会“看到它”,所以将来当最后一个依赖删除它,我也可以。 我对这两种 API 的了解都不够,无法为您提供 magic_code,但我可以说,逐步推进这一进程的最佳方法是将#ifdef USE_BOOST
与 typedef boost::system::error_code ErrorCodeType;
结合使用和#else
和typedef std::error_code ErrorCodeType;
。然后您可以对您的代码库进行渐进式更改,以便使用相同的接口调用支持两者,然后当所有这些都与 USE_BOOST
undefined 一起使用时,您可以使切换永久化。否则,您最终只会在最终被遗忘的侧流上工作。
【参考方案1】:
我有同样的问题,因为我想使用 std::error_code
,但同时也在使用其他使用 boost::system::error_code
的 boost 库(例如 boost ASIO)。接受的答案适用于 std::generic_category()
处理的错误代码,因为它们是 boost 的通用错误代码的简单转换,但它不适用于您想要处理自定义错误类别的一般情况。
所以我创建了以下代码作为通用boost::system::error_code
-to-std::error_code
转换器。它通过为每个 boost::system::error_category
动态创建一个 std::error_category
shim 来工作,将调用转发到底层 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()) );
【讨论】:
你还在用这个代码吗?它是否足够有用,可能有助于提升? @sehe AFAIK 它仍在使用中。我可以看到它是对 boost 的一个有用的补充,因为从概念上讲,错误代码的 boost 和 std 版本做完全相同的事情,只是由于类型系统而不兼容。在这种情况下,尽管直接在 boost 错误类别类中实现可能会更好。这将消除对互斥锁和映射的需要,并使转换成为 noexcept,但每个类别会增加几个字节。或者它可以直接从 std 派生,因为您可能还希望能够从 std->boost 获得?【参考方案2】:由于 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
【讨论】:
谢谢,我使用以下代码让它工作:std::make_error_code(static_cast<:errc::errc>(err.value())) - 错误是一个实例/ boost::system::error_code 的参考。【参考方案3】:我将上述解决方案改编为更短的解决方案。 使用 thread_local std::map 将类别名称映射到其实例,因此不需要锁定。
限制是您不能在线程之间传递错误代码,因为类别指针会有所不同。(如果您不想使用 thread_local 存储,将其转换为锁定函数很简单)
而且我喂它更紧凑。
#include <iostream>
#include <map>
#include <boost/system/system_error.hpp>
namespace std
error_code make_error_code(boost::system::error_code error)
struct CategoryAdapter : public error_category
CategoryAdapter(const boost::system::error_category& category)
: m_category(category)
const char* name() const noexcept
return m_category.name();
std::string message(int ev) const
return m_category.message(ev);
private:
const boost::system::error_category& m_category;
;
static thread_local map<std::string, CategoryAdapter> nameToCategory;
auto result = nameToCategory.emplace(error.category().name(), error.category());
auto& category = result.first->second;
return error_code(error.value(), category);
;
int main()
auto a = boost::system::errc::make_error_code(boost::system::errc::address_family_not_supported);
auto b = std::make_error_code(a);
std::cout << b.message() << std::endl;
【讨论】:
以上是关于是否可以将 boost::system::error_code 转换为 std:error_code?的主要内容,如果未能解决你的问题,请参考以下文章
是否可以将 MPMoviePlayerController 静音?