Sto codificando una piccola libreria e ho dei problemi con la progettazione della gestione delle eccezioni. Devo dire che sono (ancora) confuso da questa caratteristica del linguaggio C ++ e ho cercato di leggere il più possibile sull'argomento per capire cosa avrei dovuto fare per lavorare correttamente con le classi di eccezione.
Ho deciso di utilizzare un system_error
tipo di approccio prendendo ispirazione dall'implementazione STL della future_error
classe.
Ho un elenco che contiene i codici di errore:
enum class my_errc : int
{
error_x = 100,
error_z = 101,
error_y = 102
};
e una singola classe di eccezione (supportata da un error_category
tipo di strutture e tutto il resto necessario dal system_error
modello):
// error category implementation
class my_error_category_impl : public std::error_category
{
const char* name () const noexcept override
{
return "my_lib";
}
std::string message (int ec) const override
{
std::string msg;
switch (my_errc(ec))
{
case my_errc::error_x:
msg = "Failed 1.";
break;
case my_errc::error_z:
msg = "Failed 2.";
break;
case my_errc::error_y:
msg = "Failed 3.";
break;
default:
msg = "unknown.";
}
return msg;
}
std::error_condition default_error_condition (int ec) const noexcept override
{
return std::error_condition(ec, *this);
}
};
// unique instance of the error category
struct my_category
{
static const std::error_category& instance () noexcept
{
static my_error_category_impl category;
return category;
}
};
// overload for error code creation
inline std::error_code make_error_code (my_errc ec) noexcept
{
return std::error_code(static_cast<int>(ec), my_category::instance());
}
// overload for error condition creation
inline std::error_condition make_error_condition (my_errc ec) noexcept
{
return std::error_condition(static_cast<int>(ec), my_category::instance());
}
/**
* Exception type thrown by the lib.
*/
class my_error : public virtual std::runtime_error
{
public:
explicit my_error (my_errc ec) noexcept :
std::runtime_error("my_namespace ")
, internal_code(make_error_code(ec))
{ }
const char* what () const noexcept override
{
return internal_code.message().c_str();
}
std::error_code code () const noexcept
{
return internal_code;
}
private:
std::error_code internal_code;
};
// specialization for error code enumerations
// must be done in the std namespace
namespace std
{
template <>
struct is_error_code_enum<my_errc> : public true_type { };
}
Ho solo un piccolo numero di situazioni in cui lancio eccezioni illustrate dall'enumerazione del codice di errore.
Quanto sopra non ha funzionato bene con uno dei miei recensori. Era dell'opinione che avrei dovuto creare una gerarchia di classi di eccezioni con una classe di base derivata std::runtime_error
perché avere il codice di errore incorporato nella condizione mescola cose - eccezioni e codici di errore - e sarebbe più noioso occuparsi di un punto di gestione; la gerarchia delle eccezioni consentirebbe anche una facile personalizzazione del messaggio di errore.
Uno dei miei argomenti era che volevo mantenerlo semplice, che la mia libreria non aveva bisogno di generare più tipi di eccezioni e che la personalizzazione è anche facile in questo caso in quanto viene gestita automaticamente - error_code
ha un error_category
associato con esso che traduce il codice al messaggio di errore corretto.
Devo dire che non ho difeso bene la mia scelta, a testimonianza del fatto che ho ancora dei malintesi riguardo alle eccezioni C ++.
Vorrei sapere se il mio design ha un senso. Quali sarebbero i vantaggi dell'altro metodo rispetto a quello che ho scelto in quanto devo ammettere che non riesco a vederlo anche io? Cosa potrei fare per migliorare?