Sto codificando una piccola libreria e sto avendo qualche problema 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 arrivare a capire cosa avrei dovuto fare per lavorare correttamente con le classi di eccezioni.
Ho deciso di utilizzare un tipo di approccio system_error
prendendo ispirazione dall'implementazione STL della classe future_error
.
Ho un'enumerazione contenente i codici di errore:
enum class my_errc : int
{
error_x = 100,
error_z = 101,
error_y = 102
};
e una singola classe di eccezioni (supportata da un tipo% dierror_category
di strutture e tutto il resto richiesto dal modello system_error
):
// 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 si adattava bene a uno dei miei revisori. Era dell'opinione che avrei dovuto creare una gerarchia di classi di eccezioni con una classe base derivata da std::runtime_error
perché avere il codice di errore incorporato nella condizione mescola cose - eccezioni e codici di errore - e sarebbe più noioso trattare con un punto di manipolazione; la gerarchia delle eccezioni consentirebbe anche una facile personalizzazione del messaggio di errore.
Uno dei miei argomenti era che volevo semplificarlo, che la mia libreria non aveva bisogno di lanciare più tipi di eccezioni e che la personalizzazione è anche facile in questo caso poiché viene gestita automaticamente - il error_code
ha un error_category
associato ad esso che traduce il codice nel messaggio di errore corretto.
Devo dire che non ho difeso bene la mia scelta, a testimonianza del fatto che ho ancora qualche malinteso riguardo alle eccezioni C ++.
Vorrei sapere se il mio progetto ha un senso. Quali sarebbero i vantaggi dell'altro metodo rispetto a quello che ho scelto, come devo ammettere che non riesco a vederlo anche io? Cosa potrei fare per migliorare?