Tôi có cùng một câu hỏi chính xác này kể từ khi tôi muốn sử dụng std::error_code
mà còn được sử dụng thư viện tăng khác sử dụng boost::system::error_code
(ví dụ như thúc đẩy ASIO) . Câu trả lời được chấp nhận làm việc cho các mã lỗi được xử lý bởi std::generic_category()
, vì chúng là một phép đúc đơn giản từ các mã lỗi chung của boost, nhưng nó không hoạt động cho trường hợp chung mà bạn muốn xử lý các danh mục lỗi tùy chỉnh.
Vì vậy, tôi đã tạo mã sau đây làm mục đích chung boost::system::error_code
-to- std::error_code
trình chuyển đổi. Nó hoạt động bằng cách tự động tạo một shim std::error_category
cho mỗi boost::system::error_category
, chuyển tiếp cuộc gọi đến danh mục Lỗi tăng cường cơ bản. Vì các danh mục lỗi cần phải là các trình đơn (hoặc ít nhất là singleton giống như trong trường hợp này), tôi không mong đợi có nhiều vụ nổ bộ nhớ.
Tôi cũng chỉ chuyển đổi đối tượng boost::system::generic_category()
để sử dụng std::generic_category()
vì chúng sẽ hoạt động giống nhau. Tôi đã muốn làm tương tự cho system_category()
, tuy nhiên trong thử nghiệm trên VC++ 10 nó in ra các thông báo sai (tôi giả sử nó sẽ in ra những gì bạn nhận được từ FormatMessage
, nhưng nó xuất hiện để sử dụng strerror
, Boost sử dụng FormatMessage
như mong đợi) .
Để sử dụng nó chỉ cần gọi BoostToErrorCode()
, được xác định bên dưới.
Chỉ là một cảnh báo, tôi vừa viết hôm nay nên nó chỉ có thử nghiệm cơ bản. Bạn có thể sử dụng nó bất kỳ cách nào bạn thích, nhưng bạn làm như vậy nguy cơ của riêng bạn.
//==================================================================================================
// 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()));
}
Bạn có muốn sử dụng hai đồng thời không? Nếu không, không phải là các giao diện là tương tự, đủ để một "tìm kiếm/thay thế" đơn giản sẽ làm điều đó? – ereOn
Điều đó là không thể. Cả hai std :: error_code và boost :: system :: error_code được sử dụng, nhưng tôi đã quản lý để trừu tượng đi tăng :: system :: error_code cho người dùng, vì vậy nó không bao giờ "nhìn thấy nó", vì vậy trong tương lai khi phụ thuộc cuối cùng loại bỏ nó để tôi có thể – Fredrik
Tôi không biết đủ về một trong hai API để cung cấp cho bạn magic_code, nhưng tôi có thể nói rằng cách tốt nhất để dần dần chuyển tiếp này sẽ là sử dụng '#ifdef USE_BOOST' kết hợp với' typedef boost :: system :: error_code ErrorCodeType; 'và' # else' với 'typedef std :: error_code ErrorCodeType;'. Sau đó, bạn có thể thực hiện các thay đổi tiến bộ đối với cơ sở mã của mình để cả hai được hỗ trợ bằng cách sử dụng cùng một cuộc gọi giao diện và sau đó khi tất cả hoạt động với 'USE_BOOST' không xác định, bạn có thể thực hiện chuyển đổi vĩnh viễn. Nếu không, bạn sẽ chỉ làm việc trên một dòng bên mà cuối cùng sẽ bị lãng quên. – Dennis