2012-04-16 20 views
14

Tôi muốn thay thế các thư viện bên ngoài (như tăng) càng nhiều càng tốt bằng các tiêu chuẩn C++ nếu chúng tồn tại và có thể, để giảm thiểu phụ thuộc, do đó tôi tự hỏi liệu có cách nào an toàn để chuyển đổi boost::system::error_code sang std::error_code. Ví dụ về mã giả:Có thể chuyển đổi tăng :: system :: error_code thành std: error_code không?

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

Điều quan trọng nhất không phải là lỗi chính xác, gần nhất có thể và cuối cùng nếu có lỗi hay không. Có giải pháp thông minh nào không?

Cảm ơn trước!

+0

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

+1

Đ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

+0

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

Trả lời

8

Vì C++ - 11 (std :: errc), boost/system/error_code.hpp ánh xạ cùng mã lỗi tới std::errc, được xác định trong tiêu đề hệ thống system_error.

Bạn có thể so sánh cả hai enums và chúng phải có chức năng tương đương vì cả hai đều xuất hiện dựa trên tiêu chuẩn POSIX. Có thể yêu cầu một diễn viên.

Ví dụ,

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 
+3

Cảm ơn, tôi đã nhận nó để làm việc bằng cách sử dụng mã sau đây: std :: make_error_code (static_cast (err.value())) - có err là một thể hiện/tham chiếu của boost :: system :: error_code. – Fredrik

7

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

Bạn vẫn sử dụng mã này? Nó có đủ hữu ích để có thể đóng góp để tăng cường? – sehe

+0

@sehe AFAIK nó vẫn đang được sử dụng. Tôi có thể thấy nó là một bổ sung hữu ích để thúc đẩy kể từ khi, khái niệm, các phiên bản tăng và std của mã lỗi làm điều tương tự và chỉ không tương thích do hệ thống kiểu. Trong trường hợp đó mặc dù nó có thể thực hiện tốt hơn trực tiếp trong các lớp thể loại lỗi tăng. Điều đó sẽ loại bỏ sự cần thiết cho mutex và bản đồ và sẽ làm cho chuyển đổi noexcept, tại các chi phí của một vài byte nhiều hơn cho mỗi thể loại. Hoặc có lẽ nó chỉ có thể xuất phát từ std trực tiếp vì bạn cũng có thể muốn để có thể đi từ std-> tăng? – Screndib

Các vấn đề liên quan