tiistai 25. lokakuuta 2022

Kuinka monella tavalla virheen voi käsitellä

Ethan McCue esittelee blogissaan 11 erilaista tapaa, millä C-kielen funktio voi palauttaa virheen kutsuvalle ohjelmanosalle. McCue ottaa esimerkiksi yksinkertaisen funktion, joka tulkitsee numeron merkkijonossa ja palauttaa vastaavan arvon kokonaislukuna:
int parse_natural_base_10_number(const char* s) {
    int parsed = 0;
    for (size_t i = 0; s[i] != '\0'; i++) {
        parsed *= 10;
        parsed += s[i] - '0';
    }

    return parsed;
}

Ongelma on, miten funktio voi välittää kutsuvalle ohjelmanosalle tiedon, jos parametri s ei ole kelvollinen kokonaisluku.

McCuen esittelemät 11 tapaa ovat karkeasti käännettynä:
1. Unohda koko juttu
Jos virhe tapahtuu vain harvoin, niin ei ole hyödyllistä käyttää työaikaa siihen varautumiseen
2. Luovuta heti
Et voi kuitenkaan korjata virhettä, joten ohjelman on sama kaatua heti
3. Palauta negatiivinen luku
Jos funktio palauttaa normaalisti positiivisia lukuja, niin palauta virhetilanteessa negatiivinen tai muuten normaalin käsittelyalueen ulkopuolella oleva luku, esimerkiksi -1 tai 0x7fffffff
4. Palauta NULL-osoitin
Jos funktio palauttaa normaalisti osoittimen muistialueelle, niin palauta virhetilanteessa NULL-osoitin
5. Palauta boolean-arvo
Tallenna funktion normaali tulos parametrina annettuun osoitteeseen, ja palauta tosi tai epätosi sen mukaan, onnistuiko operaatio vai ei
6. Palauta virhekoodi
Tallenna funktion tulos parametrina annettuun osoitteeseen, ja palauta enum-arvolla tieto onnistumisesta (esim PARSE_NATURAL_SUCCESS) tai virheen tarkempi syy (esim PARSE_NATURAL_BAD_CHARACTER)
7. Palauta boolean-arvo ja virheen sijainti
Tallenna funktion tulos ja virheen sijainti parametreina annettuihin osoitteisiin, jotta funktiota kutsuva koodi voi muodostaa asiallisen virheilmoituksen, kun funktio palauttaa epätosi
8. Palauta virhekoodi ja lisätieto
Jos funktio voi epäonnistua monella eri tavalla, niin palauta virhekoodi enum-arvona ja tallenna virhekoodia koskeva yksityiskohtainen lisätieto erillisessä parametrissa annettuun osoitteeseen
9. Aseta globaali virhelippu
Tallenna virhekoodi ja virheen lisätiedot säikeen sisäiseen tai globaaliin muuttujaan, josta kutsuva koodi voi hakea ne tarvittaessa
10. Palauta tietue
Palauta aina viittaus tietueeseen (struct), joka sisältää virhekoodin, funktion normaalin paluuarvon sekä mahdollisen virheen yksityiskohtaiset lisätiedot
11. Palauta virheolio
Palauta viittaus geneeriseen virheolioon, jota voidaan pyytää muodostamaan varsinainen virheilmoitus tarvittaessa
Kaikki 11 tapaa ovat omalla tavallaan mielenkiintoisia ja suosittelen tutustumaan https://mccue.dev!

Päivitys 19.2.2023: Martin Sústrik vertaa blogissaan C++:n ja C:n virhekäsittelyä. C++ sallii käsitellä virheet myös poikkeuksina, mutta poikkeukset eivät aina ole hyvä juttu!