2012-03-18 47 views
7

我使用std :: error_code,並有一堆錯誤的定義(使用枚舉類)和註冊。std :: error_code,my_error :: check_block == my_error :: validate && my_error :: accept_block == my_error :: validate

我有一個非常普遍的錯誤,現在稱爲my_error :: validate,但要在我的庫中提供更具體的版本。一般人會想用:

if (ec == bc::error::validate) 
    // ... 

但是有時候他們可能希望看到與該的std :: ERROR_CODE相關的特定錯誤或打印錯誤消息。

// ec.message() says "check_block() failed to do XYZ" 
assert(ec == bc::error::check_block); 

我希望能夠讓這樣的:

if (ec == bc::error::validate) 
{ 
    if (ec == bc::error::check_block) 
     // bc::error::check_block is a more specific case of bc::error::validate 
} 

看來我可以以某種方式使用類別或條件?我怎麼做,而不需要定義一大堆新的錯誤枚舉?這是一個庫,所以這個庫的用戶必須使用bc :: generic_error :: validate和bc :: error :: check_block。

代碼如下:

#include <system_error> 

namespace bc { 

enum class error 
{ 
    // storage errors 
    missing_object = 1, 
    object_already_exists, 
    unspent_output, 
    // transaction_pool errors 
    bad_transaction, 
    // network errors 
    resolve_failed, 
    network_unreachable, 
    address_in_use, 
    listen_failed, 
    accept_failed, 
    bad_stream, 
    channel_stopped, 
    channel_timeout, 
    // validate 
    validate_failed, 
    check_block, 
    accept_block, 
    connect_block 
}; 

class error_category_impl 
    : public std::error_category 
{ 
public: 
    virtual const char* name() const; 
    virtual std::string message(int ev) const; 
    virtual std::error_condition default_error_condition(int ev) const; 
}; 

const std::error_category& error_category(); 

std::error_code make_error_code(error e); 
std::error_condition make_error_condition(error e); 

} // bc 

namespace std 
{ 
    template <> 
    struct is_error_code_enum<libbitcoin::error> 
     : public true_type {}; 
} 

而且恩源文件:

#include <bc/error.hpp> 

namespace bc { 

const char* error_category_impl::name() const 
{ 
    return "bitcoin"; 
} 

std::string error_category_impl::message(int ev) const 
{ 
    error ec = static_cast<error>(ev); 
    switch (ec) 
    { 
     case error::missing_object: 
      return "Object does not exist"; 
     case error::object_already_exists: 
      return "Matching previous object found"; 
     case error::unspent_output: 
      return "Unspent output"; 
     case error::bad_transaction: 
      return "Transaction failed to validate"; 
     case error::resolve_failed: 
      return "Resolving hostname failed"; 
     case error::network_unreachable: 
      return "Unable to reach remote network"; 
     case error::address_in_use: 
      return "Address already in use"; 
     case error::listen_failed: 
      return "Listen incoming connections failed"; 
     case error::accept_failed: 
      return "Accept connection failed"; 
     case error::bad_stream: 
      return "Bad stream"; 
     case error::channel_stopped: 
      return "Channel stopped"; 
     case error::channel_timeout: 
      return "Channel timed out"; 
     default: 
      return "Unknown error"; 
    } 
} 

std::error_condition 
    error_category_impl::default_error_condition(int ev) const 
{ 
    error ec = static_cast<error>(ev); 
    switch (ec) 
    { 
     case error::check_block: 
     case error::accept_block: 
     case error::connect_block: 
      //return error::validate_failed; 
      return std::errc::permission_denied; 
     default: 
      return std::error_condition(ev, *this); 
    } 
} 

const std::error_category& error_category() 
{ 
    static error_category_impl instance; 
    return instance; 
} 

std::error_code make_error_code(error e) 
{ 
    return std::error_code(static_cast<int>(e), error_category()); 
} 

std::error_condition make_error_condition(error e) 
{ 
    return std::error_condition(static_cast<int>(e), error_category()); 
} 

} // bc 

回答

9

OK我從升壓幫助:: ASIO和std :: ERROR_CODE創造者和掌握自己:克里斯Kohlhoff 。

使用ADL時,一個很好的經驗法則是它不應該需要任何限定符(在我的情況下error :: error_code_t),並且我處於錯誤的範圍。

#include <iostream> 
#include <system_error> 

namespace libbitcoin { 

namespace error 
{ 
    // Specific errors 
    enum error_code_t 
    { 
     // storage errors 
     missing_object = 1, 
     object_already_exists, 
     unspent_output, 
     // transaction_pool errors 
     bad_transaction, 
     // network errors 
     resolve_failed, 
     network_unreachable, 
     address_in_use, 
     listen_failed, 
     accept_failed, 
     bad_stream, 
     channel_stopped, 
     channel_timeout, 
     // validate 
     check_block, 
     accept_block, 
     connect_block 
    }; 

    // error_condition 
    enum error_condition_t 
    { 
     // validate 
     validate_failed = 1 
    }; 

    std::error_code make_error_code(error_code_t e); 
    std::error_condition make_error_condition(error_condition_t e); 
} 

class error_category_impl 
    : public std::error_category 
{ 
public: 
    virtual const char* name() const; 
    virtual std::string message(int ev) const; 
    virtual std::error_condition default_error_condition(int ev) const; 
}; 

const std::error_category& error_category(); 

} // libbitcoin 

namespace std 
{ 
    template <> 
    struct is_error_code_enum<libbitcoin::error::error_code_t> 
     : public true_type {}; 

    template <> 
    struct is_error_condition_enum<libbitcoin::error::error_condition_t> 
     : public true_type {}; 
} 

// ------------------------------------------------------------------- 

namespace libbitcoin { 

namespace error { 
std::error_code make_error_code(error_code_t e) 
{ 
    return std::error_code(static_cast<int>(e), error_category()); 
} 

std::error_condition make_error_condition(error_condition_t e) 
{ 
    return std::error_condition(static_cast<int>(e), error_category()); 
} 
} 

const char* error_category_impl::name() const 
{ 
    return "bitcoin"; 
} 

std::string error_category_impl::message(int ev) const 
{ 
    //error ec = static_cast<error>(ev); 
    switch (ev) 
    { 
     case error::missing_object: 
      return "Object does not exist"; 
     case error::object_already_exists: 
      return "Matching previous object found"; 
     case error::unspent_output: 
      return "Unspent output"; 
     case error::bad_transaction: 
      return "Transaction failed to validate"; 
     case error::resolve_failed: 
      return "Resolving hostname failed"; 
     case error::network_unreachable: 
      return "Unable to reach remote network"; 
     case error::address_in_use: 
      return "Address already in use"; 
     case error::listen_failed: 
      return "Listen incoming connections failed"; 
     case error::accept_failed: 
      return "Accept connection failed"; 
     case error::bad_stream: 
      return "Bad stream"; 
     case error::channel_stopped: 
      return "Channel stopped"; 
     case error::channel_timeout: 
      return "Channel timed out"; 
     case error::check_block: 
      return "Checkblk"; 
     default: 
      return "Unknown error"; 
    } 
} 

std::error_condition 
    error_category_impl::default_error_condition(int ev) const 
{ 
    //error ec = static_cast<error>(ev); 
    switch (ev) 
    { 
     case error::check_block: 
     case error::accept_block: 
     case error::connect_block: 
      return std::error_condition(error::validate_failed, *this); 
     default: 
      return std::error_condition(ev, *this); 
    } 
} 

const std::error_category& error_category() 
{ 
    static error_category_impl instance; 
    return instance; 
} 

} // libbitcoin 

using namespace libbitcoin; 

#include <assert.h> 

int main() 
{ 
    std::error_code ec = error::check_block; 
    assert(ec == error::validate_failed); 
    assert(ec == error::check_block); 
    std::cout << ec.message() << std::endl; 
    //ec = error::missing_object; 
    return 0; 
} 
+2

非常有趣!關於如何在C++ 11中實現自定義錯誤消息,現在沒有太多的例子,所以你的答案是非常寶貴的資源。爲了記錄我確實研究了一下你的問題,意識到你必須驗證一個error_condition並將其映射到accept_block/check_block等,但無法確定如何。看到最終在這種情況下,純枚舉比C++ 11枚舉類更好,因爲它們在其名稱空間中具有全局可見性! – 2012-03-19 23:41:12

+0

另外,看到設計得非常好,可以支持像你這樣的中等複雜的錯誤框架,並具有此錯誤代碼分區業務。我只是希望他們在標準化過程中找到了一種方法來簡化系統,但是仍然很難弄清楚它是如何一蹴而就的。 – 2012-03-19 23:52:08

+1

是的,我認爲這是一個很酷的系統:)所有感興趣的源代碼都可以在這裏找到:http://gitorious.org/libbitcoin/libbitcoin/trees/master(參見include/error.hpp和src/error.cpp) – genjix 2012-03-20 12:11:38