2015-10-23 16 views
6

Se la risorsa che si richiede di acquisire nella costruzione di un oggetto può fallire, è possibile eseguire RAII, se le eccezioni sono proibite da uno standard di codifica locale?È possibile eseguire RAII senza eccezioni?

In tal caso, qual è il modo canonico per gestire il fallimento dell'acquisizione delle risorse in questo caso?

+1

Quando non riesco ad utilizzare eccezioni per qualsiasi motivo, finisco per creare metodi di inizializzazione per oggetti che possono fallire nella costruzione. Quindi il costruttore non farebbe altro che una banale installazione. Quindi posso avere un metodo Init che fa l'effettiva acquisizione delle risorse e restituisce vero/falso di conseguenza. –

+2

Le tue linee guida non dicono nulla riguardo a situazioni del genere? Dopo tutto, è * un * linguaggio C++ molto comune. E se le linee guida lo consentono, puoi sempre utilizzare un sistema simile alla [libreria I/O standard] (http://en.cppreference.com/w/cpp/io) e ai suoi flussi, ad es. che i flussi possono essere usati in condizioni per verificare la presenza di errori o come i flussi di file che hanno una funzione 'is_open'. –

+0

@JoachimPileborg Non penso che sia stato considerato nelle linee guida, potrebbero richiedere un aggiornamento (anche se è un sistema embedded in tempo reale, e le eccezioni non sono piaciute!) In ogni caso, la mia sensazione è che non è possibile, quindi solo per Per questa domanda vorrei che il mio sentimento fosse confermato! :-) – Joe

risposta

6

non volevo andare con l'approccio oggetto non valido in generale, perché vorrei considerare questo come cattivo design. Dopo la costruzione, l'oggetto deve trovarsi in uno stato in cui sono stabiliti gli invarianti (che è l'unico scopo che un costruttore deve servire). Considerare una classe strange_vector che implementa qualcosa come std::vector, ma dopo aver chiamato strange_vector<int>(10, 0), l'oggetto si troverebbe in uno stato inutilizzabile perché l'allocazione non è riuscita.

Invece vorrei dichiarare costruttori privati ​​e utilizzare un metodo factory che restituisce un optional:

class file 
{ 
public: 
    ~file() {fclose(m_file);} 

    static std::optional<file> open(std::string const& filename) 
    { 
     auto f = fopen(filename.c_str(), "r"); 
     if (f) 
     { 
      return std::make_optional<file>(f); 
     } 
     else 
     { 
      return std::nullopt; 
     } 
    } 

private: 
    file(FILE* file); 
    FILE* m_file; 
}; 

Uno dei maggiori vantaggi di gestione delle eccezioni è (oltre disaccoppiamento gestione degli errori e normale percorso di codice) che non si può ignorali accidentalmente. Se lo si desidera, è possibile creare una classe personalizzata simile a optional che, quando non è inizializzata con un oggetto valido, registra un messaggio di errore e termina il programma (o qualsiasi altra ragionevole gestione degli errori). Penso che ci sia un talk from A. Alexandrescu about systematic error handling dove implementa una classe Expected<T> che contiene un valore di tipo T o un'eccezione. Puoi usare questa base e invece di aggiungere il tuo errore di gestione lì.

std::optional non fa ancora parte dello standard, ma è possibile ottenere facilmente implementazioni come parte di compilatori recenti, in boost o in altre librerie.

+0

Hai mescolato locale 'f' e membro' m_file', vero? –

+0

@ MatthäusBrandl Sì, grazie per aver individuato questo. – Jens

1

È sempre possibile creare un metodo bool valid(void). Il costruttore può quindi impostare la condizione appropriata e nel codice, è possibile verificare dopo la costruzione se questo ha funzionato o meno.

class foo 
{ 
public: 
    foo(const char *Filename) 
    { 
     mValid = false; 

     if(fopen(Filename) == NULL) 
      return; 

     mValid = true; 
    } 

    bool valid(void) { return mValid; } 

    private: 
     bool mValid; 
}; 

void myfunc(void) 
{ 
    foo fl("myfile"); 
    if(!fl.valid()) 
    { 
     printf("Error\n"); 
     return; 
    } 
} 
+0

Non mi piace molto questo approccio. Cosa dovrebbe succedere se chiamo un metodo di foo che utilizza il file, ma l'inizializzazione è fallita? I costruttori che non hanno stabilito gli invarianti dell'oggetto sono un no-go. – Jens

+0

Se si chiama un metodo che utilizza foo, deve verificare internamente che il file sia effettivamente valido. Beh, è ​​brutto, ma almeno garantisce che l'oggetto stesso funzioni correttamente. – Devolus

+0

Questo aggiunge controllo per ogni chiamata di metodo. Non sono sicuro che l'OP voglia questo nel suo dominio incorporato, e non penso che lo vorrei in generale, ad es. quando una classe vettoriale con questo approccio che controlla ogni accesso ... – Jens

Problemi correlati