std::error_code, my_error::check_block == my_error::validate && my_error::accept_block == my_error::validate

Я использую std::error_code, и у меня определена и зарегистрирована куча ошибок (с использованием класса enum).

У меня есть очень общая ошибка, которая теперь называется 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

person genjix    schedule 18.03.2012    source источник


Ответы (2)


ОК, мне помог сам создатель и мастер boost::asio и std::error_code: Chris 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;
}
person genjix    schedule 19.03.2012
comment
Очень интересно ! Сейчас не так много примеров того, как реализовать собственное сообщение об ошибке в С++ 11, поэтому ваш ответ является очень ценным ресурсом. Для справки, я немного изучил ваш вопрос, понял, что вам нужно проверить error_condition и сопоставить его с accept_block/check_block и т. д., но не мог понять, как это сделать. Довольно забавно видеть, что в конечном итоге в этом случае простое перечисление лучше, чем класс перечисления С++ 11, именно потому, что они имеют глобальную видимость в своем пространстве имен! - person Thomas Petit; 20.03.2012
comment
Кроме того, очень приятно видеть, что ‹system_error› достаточно хорошо разработана для поддержки умеренно сложной структуры ошибок, такой как ваша, с этим бизнесом по разделению кода ошибки. Я просто хотел бы, чтобы они нашли способ во время стандартизации как-то упростить систему, все еще очень трудно понять, как она работает с первого взгляда. - person Thomas Petit; 20.03.2012
comment
Да, я думаю, что это довольно крутая система :) Исходный код доступен здесь для всех заинтересованных: gitorious. org/libbitcoin/libbitcoin/trees/master (см. include/error.hpp и src/error.cpp) - person genjix; 20.03.2012
comment
Было бы неплохо: теперь я разделил error::check_block на еще более мелкие ошибки, и было бы хорошо каким-то образом связать эти ошибки с категорией error::check_block, которая эквивалентна error::validate_block. Я думаю, что это возможно, переопределив эквивалентную (...) функцию в категории ошибок, но на данный момент это не является большой проблемой. - person genjix; 20.03.2012
comment
@genix Спасибо за приведенный здесь пример, однако после того, как я протестировал код, я обнаружил, что assert(error::missing_object != error::validate_failed); потерпит неудачу, чего вы не ожидаете, верно? А я изучил код и нашел, что причина кроется в дефолте: return std::error_condition(ev, *this);. Мне интересно, это ошибка? - person iericzhou; 26.03.2018

Для всех, кто может быть заинтересован в этой теме «код_ошибки», «условие_ошибки», я настоятельно рекомендую эти 2 руководства, которые могут быть лучшими, которые я когда-либо видел: Код ошибки, Условие ошибки.

person iericzhou    schedule 26.03.2018