Gestione degli errori di basso livello

3

Sto lavorando con un codice di basso livello (ovvero intendo codice che non può usare le eccezioni C ++ e / o la libreria standard) che fa un uso pesante delle classi.
Fondamentalmente, ogni classe contiene un metodo bool initialize(); che viene chiamato subito dopo l'istanziazione per inizializzare tutti i suoi componenti, gli oggetti sottostanti e così via.
Questo perché i costruttori in C ++ non possono restituire un valore. Inoltre, tutti i metodi che allocano memoria, utilizzano API di sistema che potrebbero non funzionare, ecc. Devono essere controllati per ottenere un valore positivo.
Tuttavia, questo approccio diventa molto fastidioso dopo un po '.
Considera il seguente codice:

bool createHelloWorldString(String* string)
{
    String str1;
    if (!str1.initialize())
        return false;

    String str2;
    if (!str2.initialize())
        return false;
    // Need to check return value as this method may fail because it dynamically allocates memory
    if (!str1.set("Hello "))
        return false;

    if (!str2.set("world"))
        return false;

    if (!str1.append(&str2))
        return false;

    return string->append(&str1);
}

Nota: Questo potrebbe non essere il miglior esempio, ma mostra chiaramente dove si trova il problema.

Ci sono altri modi per gestire gli errori o sono bloccato con questo?

    
posta man 19.02.2016 - 19:44
fonte

4 risposte

4

Non è esattamente raccomandato per C ++ quando puoi usare tutte le funzionalità del linguaggio, ma puoi usare il preprocessore per darti una mano. Supponendo che non sia necessario eseguire alcuna pulizia, è possibile definire una macro per

if (function_call) return false;

bit, e usa quello. Se devi fare un po 'di pulizia, dovrò raccomandare di andare contro una saggezza popolare ampiamente condivisa (potresti aver sentito parlare del bit "Goto considerato dannoso") e raccomandare l'uso del goto, anche se lo consiglierei comunque avvolgendolo anche con i macro.

Ad esempio, per risolvere lo stesso problema che stai vedendo in C, avevamo qualcosa del tipo:

#define CHECK(A) if (!(A)) goto End;

bool function_a ()
{
    CHECK(function_b());
    // Do more stuff here

    End:
    // Do cleanup here
}

Questo ti dà qualcosa di simile alla gestione delle eccezioni in-function di un uomo povero. Non è così completo, ma considerando che hai solo errori / OK con un ritorno booleano, dovrebbe essere sufficiente. Quando lavoravo in un'azienda in cui C era la lingua prescelta, il valore di errore era un numero intero da un insieme di valori predefiniti e la funzione di controllo eseguiva la registrazione e il controllo (almeno durante la compilazione per il debug). Quest'ultimo bit ci ha risparmiato un sacco di ore per trovare dove si sono verificati problemi.

    
risposta data 19.02.2016 - 20:02
fonte
2

A livello di codice, non hai molte buone opzioni. Se è possibile utilizzare C ++ 11, sono disponibili alcune tecniche di gestione degli errori in stile funzionale, come concatenamento di opzioni .

Tuttavia, generalmente un codice di basso livello come quello utilizza altri mezzi per gestire errori fatali nelle chiamate di sistema, come allocazione della memoria, errori di segmentazione e così via. Questo di solito comporta la creazione di registri di interrupt ed è disponibile anche in microcontrollori super-economici. Dovresti davvero approfittare di queste strutture e lasciare il fastidioso controllo booleano in stile C per gli errori nel codice spazio utente.

    
risposta data 19.02.2016 - 20:18
fonte
0

Basically, every class contains a bool initialize(); method that is called right after instantiation to initialize all its components, underlying objects and such.

Yikes!

This is done because constructors in C++ can't return a value.

Non ne hanno bisogno. Alla fine hai un oggetto completamente formato che puoi usare o hai un'eccezione da catturare. Quali altri casi utili esistono?

Also every method that allocates memory, uses system API that may fail etc. must be checked for positive value.

Sì e se quelle cose falliscono, il lancio di eccezioni e RAII si srotoleranno la creazione dell'oggetto in modo da non avere un oggetto semi-formato in agguato aspettando il disastro.

However, this approach becomes very annoying after a while.

Mi infastidisce solo leggerlo.

I am working with some low-level (by that I mean code that can't use C++ exceptions and/or the standard library) code that makes heavy use of classes.

Oh. Bene, ok.

Are there any other ways to handle errors or am I stuck with this?

Per la maggior parte del tempo ne sei bloccato per definizione, per quanto ne so. C'è una soluzione per migliorare tutto e questa soluzione è stata inventata proprio per rendere tutto questo migliore ... ma quella soluzione è un'eccezione.

Una cosa che potresti provare è l'approccio IOStreams di impostare internamente un "flag di errore" che rende ogni operazione successiva su quell'oggetto un no-op. Quindi non hai bisogno di tutti quei return false e probabilmente puoi semplicemente fare un if (!obj.is_good()) return false; in uno, forse due posti verso la fine della tua funzione. Ma eseguirai un sacco di chiamate di funzione che non fanno nulla; se questo è un problema per te, solo tu puoi dirlo.

    
risposta data 19.02.2016 - 20:00
fonte
0

Sono d'accordo con questo post - l'incubo che stai descrivendo è esattamente il motivo per cui sono state inventate le eccezioni, e perché la convenzione decreta che il codice costruttore (inizializzazione e creazione di invarianti) si trova nel costruttore di un oggetto e non in un metodo initialise() o in un certo tipo di rot.

Se il tuo codice non deve lasciare che le eccezioni escano per qualche motivo, allora una soluzione è catturare le eccezioni nel livello dell'interfaccia e tradurle in modo appropriato, cioè in valori restituiti dall'errore o simili.

    
risposta data 19.02.2016 - 20:08
fonte

Leggi altre domande sui tag