Gestione degli errori in stile ruggine in C ++

7

Ho letto alcuni articoli su come Rust gestisce gli errori usando il tipo Result<T, E> e per me sembra una soluzione ibrida di best-of-both-world (eccezioni e codici di ritorno) che può essere molto utile. Penso che sarebbe particolarmente utile per le API in cui non si sa in anticipo se determinati errori sono eccezionali e dovrebbero essere generati, o che sono semplicemente un'indicazione di qualcosa che va storto in un modo "previsto". Il ritorno di Result consente di trasferire tale decisione agli utenti dell'API in modo non troppo intrusivo. Quindi ora sto discutendo se introdurlo o meno in un progetto C ++. La classe dei risultati potrebbe essere implementata in modo approssimativo come segue:

template< class T, class E = std::exception >
class result
{
public:
  //constructors etc

  operator bool() const
  {
    return !!res;
  }

  T& operator * ( )
  {
    if( !res )
    {
      assert( ex );
      throw *ex;
    }
    return *res;
  }

  const E& exception() const
  {
    assert( ex );
    return *ex;
  }

private:
  std::optional< T > res;
  std::optional< E > ex;
};

Quindi, c'è una funzione

result< int > Read();

i chiamanti possono usare lo stile 'error-code':

const auto result = Read();
if( !result )
{
  log.debug() << "failed read" << result.exception();
  return false; //or again return a Result
}
doSomething( *result );

o stile "eccezione":

try
{
  doSomething( *Read() );
}
catch( const std::Exception& e )
{
  //do what's needed
}

o decidi che non sanno come gestire le eccezioni da Leggi e lascia che il loro chiamante a sua volta si occupi di esso

doSomething( *Read() ); //throws if !result

Questa sembra una buona idea in generale, e ci sono principi simili usati nel codice C ++ esistente? O ci sono seri svantaggi che non vedo al momento? Come è troppo complicato verso gli utenti perché ci sono troppe opzioni o forse perché non è un tipico sistema usato in C ++?

    
posta stijn 13.07.2015 - 13:19
fonte

2 risposte

4

Esistono notevoli differenze tra il lancio di eccezioni e la restituzione di oggetti di errore, in particolare

    Le eccezioni
  • creano molti nuovi percorsi di flusso di codice che non sono esplicitamente visibili nel codice sorgente,
  • i chiamanti non hanno idea di quali tipi di errori possono aspettarsi,
  • si propagano automaticamente e
  • rendono il codice molto meno dettagliato e più facile da leggere e scrivere.

Rust cerca di ottenere il meglio da entrambi i mondi escludendo le eccezioni (questo elimina i primi due punti) e avendo la macro try! (si occupa di propagazione automatica e verbosità).

La tua soluzione riguarda solo i primi due punti e non esiste un modo semplice per avere questi ultimi due. Il tuo operatore * non è un equivalente di try! . L'equivalente di

x = try!(foo());

semplicemente non è possibile fare in C ++ (senza estensioni di lingua).

Detto questo, sentiti libero di usare la tua classe, anche se come sottolinea @Deduplicator, la tua classe Result può contenere solo oggetti di eccezione di tipo dinamico std::exception , che probabilmente non è quello che intendi. Il modo più semplice per realizzare ciò che vuoi è utilizzare std::exception_ptr per contenere l'oggetto eccezione.

O, meglio ancora, basta usare le eccezioni: è per questo che il linguaggio è stato progettato, dopotutto.

    
risposta data 13.07.2015 - 17:05
fonte
1

Beh, potrebbe essere concepibilmente utile. Se la funzione non dovrebbe comunque aver usato eccezioni.

Ma il tuo tipo è completamente sbagliato:

  1. Devi assegnare dinamicamente l'eccezione (o almeno consentire che venga allocata dinamicamente) per consentire la restituzione di un sottotipo.
  2. Evitare di impilare gli optionals mutualmente esclusivi uno sopra l'altro, che semplicemente spreca spazio. E lo stack-space è in realtà un po 'prezioso. Dai un'occhiata a Boost.Variant .

Se correggi il n. 2, potresti essere in grado di eseguire un'ottimizzazione di piccola eccezione per il n. 1 e quindi evitare l'allocazione dinamica il più delle volte.

    
risposta data 13.07.2015 - 14:35
fonte

Leggi altre domande sui tag