Estoy usando std :: error_code y tengo un montón de errores definidos (usando la clase enum) y registrado.std :: error_code, my_error :: check_block == my_error :: validate && my_error :: accept_block == my_error :: validate
Tengo un error muy genérico ahora llamado my_error :: validate, pero quiero proporcionar versiones más específicas en mi biblioteca. En general, la gente va a querer usar:
if (ec == bc::error::validate)
// ...
Sin embargo a veces es posible que deseen ver el error específico asociado con ese std :: código_error o imprimir el mensaje de error.
// ec.message() says "check_block() failed to do XYZ"
assert(ec == bc::error::check_block);
Quiero ser capaz de permitir que algo como:
if (ec == bc::error::validate)
{
if (ec == bc::error::check_block)
// bc::error::check_block is a more specific case of bc::error::validate
}
Parece alguna manera puedo usar categorías o condiciones? ¿Cómo puedo hacer eso sin tener que definir un montón de nuevas enumeraciones de errores? Es para una biblioteca, por lo que sería una molestia para el usuario de esa biblioteca tener que usar bc :: generic_error :: validate y bc :: error :: check_block.
Código es el siguiente:
#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 {};
}
Y el archivo de origen TU:
#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
Muy interesante! No hay muchos ejemplos en este momento sobre cómo implementar un mensaje de error personalizado en C++ 11, por lo que su respuesta es un recurso muy valioso. Para el registro, sí estudié un poco su pregunta, me di cuenta de que debe validar una condición de error y asignarla a accept_block/check_block, etc., pero no pude entender cómo. ¡Es bastante divertido ver que eventualmente en este caso plain enum es mejor que C++ 11 enum class precisamente porque tienen visibilidad global en su espacio de nombres! –
También es genial ver que está suficientemente bien diseñado para admitir un marco de error moderadamente complejo como el suyo, con este negocio de particionado de código de error. Desearía que encontraran una manera durante la estandarización para simplificar el sistema un poco más de alguna manera, todavía es realmente difícil descubrir cómo funciona a primera vista. –
Sí, creo que es un sistema bastante genial :) El código fuente está disponible aquí para todos los interesados: http://gitorious.org/libbitcoin/libbitcoin/trees/master (vea include/error.hpp y src/error.cpp) – genjix