Ho un'API scritta in C, che produce un risultato restituendo un puntatore alla memoria allocata.
Per usarlo con C ++ (C ++ 11) ho avvolto le chiamate di funzione negli oggetti, che mantengono il risultato in std::shared_ptr
. Fin qui tutto bene.
Tuttavia, la libreria C offre funzioni due per ogni operazione. Uno produce probabilmente un errore, l'altro mai. Chiamiamoli su some_pod * do_it_with_error(Parameter ..., Error **)
e
some_pod * do_it_without_error(Parameter ...)
Posso passare l'indirizzo di un puntatore Error *
alla prima funzione e se c'è un errore non sarà più NULL
in seguito.
Per risolvere ciò ho pensato a due diverse implementazioni.
Innanzitutto, potrei SFINAE per scegliere tra le funzioni with_error
e without_error
, in questo modo:
template<typename NoThrow = void>
struct do_it {
public:
void operator()(...)
{
Error * error = NULL;
m_result = std::shared_ptr<some_pod>(do_it_with_error(..., &error));
if (error) {
throw(MyExceptionWithError(error));
}
}
private:
std::shared_ptr<some_pod> m_result;
};
template<>
class do_it<std::nothrow_t> {
public:
void operator()(...) noexcept
{
if (! m_result) {
m_result = std::shared_ptr<some_pod>(do_it_without_error(...));
}
}
private:
std::shared_ptr<some_pod> m_result;
};
Utilizzo:
do_it<> di_error;
try {
di_error(...); // might throw
} catch (...) {}
do_it<std::nothrow_t> di_no_error;
di_no_error(...); // won't throw for sure
Tuttavia, poiché il risultato è incluso in std::shared_ptr
, ci sarà anche
un metodo get()
:
const std::shared_ptr<some_pod> & get(void) { return m_result; }
Per il controllo degli errori, potrei semplicemente implementare un altro metodo check
.
L'implementazione predefinita sarebbe ora simile a questa:
struct do_it {
public:
// will never throw
const std::shared_ptr<some_pod> &
get(void) noexcept
{
if (! m_result) {
m_result = std::shared_ptr<some_pod>(do_it_without_error(...));
}
return m_result;
}
// might throw
do_it &
check(void)
{
if (! m_result) {
Error * error = NULL;
m_result = std::shared_ptr<some_pod>(do_it_with_error(..., &error));
if (error) {
throw ...;
}
}
return *this;
}
private:
std::shared_ptr<some_pod> m_result;
};
Utilizzo:
do_it di_error;
try {
auto result = di_error.check().get();
} catch (...) {}
do_it di_no_error;
di_no_error.get();
Quindi, entrambe le implementazioni sembrano essere ugualmente buone (o cattive) per me.
Come potrei decidere quale usare.
Quali sono i pro e i contro?