Frage Wie man std :: Ausnahmen mit variablen Nachrichten wirft?


Dies ist ein Beispiel dafür, was ich häufig mache, wenn ich einer Ausnahme einige Informationen hinzufügen möchte:

std::stringstream errMsg;
errMsg << "Could not load config file '" << configfile << "'";
throw std::exception(errMsg.str().c_str());

Gibt es einen schöneren Weg, es zu tun?


75
2017-09-04 10:48


Ursprung


Antworten:


Hier ist meine Lösung:

class Formatter
{
public:
    Formatter() {}
    ~Formatter() {}

    template <typename Type>
    Formatter & operator << (const Type & value)
    {
        stream_ << value;
        return *this;
    }

    std::string str() const         { return stream_.str(); }
    operator std::string () const   { return stream_.str(); }

    enum ConvertToString 
    {
        to_str
    };
    std::string operator >> (ConvertToString) { return stream_.str(); }

private:
    std::stringstream stream_;

    Formatter(const Formatter &);
    Formatter & operator = (Formatter &);
};

Beispiel:

throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData);   // implicitly cast to std::string
throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData >> Formatter::to_str);    // explicitly cast to std::string

28
2017-09-04 11:32



Die Standardausnahmen können aus a erstellt werden std::string:

#include <stdexcept>

char const * configfile = "hardcode.cfg";
std::string const anotherfile = get_file();

throw std::runtime_error(std::string("Failed: ") + configfile);
throw std::runtime_error("Error: " + anotherfile);

Beachten Sie, dass die Basisklasse std::exception kann nicht so konstruiert sein; Sie müssen eine der konkreten abgeleiteten Klassen verwenden.


129
2017-09-04 10:50



Es gibt verschiedene Ausnahmen wie runtime_error, range_error, overflow_error, logic_erroretc .. Sie müssen die Zeichenfolge in ihren Konstruktor übergeben, und Sie können, was Sie möchten, an Ihre Nachricht verketten. Das ist nur eine String-Operation.

std::string errorMessage = std::string("Error: on file ")+fileName;
throw std::runtime_error(errorMessage);

Sie können auch verwenden boost::format so was:

throw std::runtime_error(boost::format("Error processing file %1") % fileName);

21
2017-09-04 10:53



Die folgende Klasse kommt mir recht praktisch:

struct Error : std::exception
{
    char text[1000];

    Error(char const* fmt, ...) __attribute__((format(printf,2,3))) {
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(text, sizeof text, fmt, ap);
        va_end(ap);
    }

    char const* what() const throw() { return text; }
};

Verwendungsbeispiel:

throw Error("Could not load config file '%s'", configfile.c_str());

13
2017-09-04 10:53



Verwenden Sie den String-Literal-Operator, wenn C ++ 14 (operator ""s)

using namespace std::string_literals;

throw std::exception("Could not load config file '"s + configfile + "'"s);

oder definieren Sie Ihre eigenen wenn in C ++ 11. Zum Beispiel

std::string operator ""_s(const char * str, std::size_t len) {
    return std::string(str, str + len);
}

Ihre throw-Anweisung sieht dann so aus

throw std::exception("Could not load config file '"_s + configfile + "'"_s);

das sieht nett und sauber aus.


9
2017-09-09 09:05



Ein wirklich schöner Weg wäre, eine Klasse (oder Klassen) für die Ausnahmen zu erstellen.

Etwas wie:

class ConfigurationError : public std::exception {
public:
    ConfigurationError();
};

class ConfigurationLoadError : public ConfigurationError {
public:
    ConfigurationLoadError(std::string & filename);
};

Der Grund ist, dass Ausnahmen viel mehr sind als nur eine Zeichenfolge zu übertragen. Durch die Bereitstellung verschiedener Klassen für die Fehler geben Sie Entwicklern die Möglichkeit, einen bestimmten Fehler in einer entsprechenden Weise zu behandeln (nicht nur eine Fehlermeldung anzuzeigen). Personen, die Ihre Ausnahme abfangen, können so spezifisch sein, wie sie benötigen, wenn Sie eine Hierarchie verwenden.

a) Man muss den spezifischen Grund kennen

} catch (const ConfigurationLoadError & ex) {
// ...
} catch (const ConfigurationError & ex) {

a) ein anderer möchte Details nicht wissen

} catch (const std::exception & ex) {

Hier finden Sie einige Anregungen zu diesem Thema https://books.google.ru/books?id=6tjfmnKhT24C Kapitel 9

Sie können auch eine benutzerdefinierte Nachricht angeben, aber seien Sie vorsichtig - Es ist nicht sicher, eine Nachricht mit beiden zu verfassen std::string oder std::stringstream oder jede andere Möglichkeit, die eine Ausnahme verursachen kann.

Generell gibt es keinen Unterschied, ob Sie im Konstruktor der Exception oder kurz vor dem Werfen Speicher zuweisen (mit C ++ - Strings arbeiten) - std::bad_alloc Ausnahme kann vor die eine geworfen werden, die Sie wirklich wollen.

Ein auf dem Stack zugeordneter Puffer (wie in Maxims Antwort) ist also ein sicherer Weg.

Es ist sehr gut erklärt bei http://www.boost.org/community/error_handling.html

Also, der schönere Weg wäre ein bestimmter Typ der Ausnahme und vermeidet es, die formatierte Zeichenfolge zu erstellen (zumindest beim Werfen).


1
2017-09-08 08:50