C++ Komfort-Exception



  • Hallo Zusammen,

    ich stehe öfter vor dem Problem, dass ich beim werfen einer (STL) Exception gerne ein paar Zusatzinformationen mitgeben möchte. Diese baue ich dann umständlich per Stringstream zusammen. Ich hätte aber gerne einen Einzeiler am liebsten ohne irgendwelche Makros zu basteln. Ist es möglich eine von std::runtime_error abgeleitete Klasse zu implementieren, mit der ein Aufruf in folgenden oder ähnlicher Formen möglich ist:

    MyException::throw("Simple Exception");
    
    int errorCode = 42
    MyException::throw("Exception with details") << "Error code: " << errorCode;
    

    Die gesamte Information soll per std::runtime_error.what() verfügbar sein. Meine bisherigen Basteleien waren nicht erfolgreich...



  • Klar ist das möglich, du kannst von std::runtime_error ableiten und eigene Attribute definieren:

    class MyException : public std::runtime_error
    {
    public:
       MyException( const char* message, unsigned int error_code ) :
          std::runtime_error( message ),
          ErrorCode_( error_code )
       {
       }
    
       unsigned int error_code() const
       {
          return ErrorCode_;
       }
    private:
       unsigned int ErrorCode_;
    };
    
    void f()
    {
       throw MyException( "Du hast doofe Ohren!", 43 );
    }
    

    Ansonsten kannst du strings auch ohne stringstream zusammenbauen:

    void f()
    {
       throw std::runtime_error( "Exeption with details, Error code: " + to_string( error_code) + "." );
    }
    


  • Die erste Lösung ist mir nicht generisch genug. Ich möchte gerne beliebige Argumente übergeben.

    Die zweite Lösung ist perfekt. Die Funktion to_string() ist bisher an mir vorbei gegangen. 🙂 Danke!



  • Also ich mach das mit fmt:

    #include <fmt/format.h>
    class RuntimeError : public std::runtime_error {
    public:
        template<class... Args>
        explicit RuntimeError(std::string_view msg, Args&&... args)
            : std::runtime_error{fmt::format(msg, std::forward<Args>(args)...)} { }
    };
    
    // ..
    throw RuntimeError("Some {} error message with {} arguments ({} in total)", "complex", "many", 3);
    

    Lohnt sich aber eigentlich nur, wenn man die Bibliothek sowieso in Verwendung hat.

    Dein Ansatz als Klasse ist schwierig, weil man dem std::runtime_error schon im c-tor die Message übergeben muss, bevor man überhaupt Zeit hat die Nachricht zusammenzufügen. Da würde ich eher eine Funktion 'error' o. Ä. mit variadic templates schreiben, welche die Nachricht zusammenfügt und dann intern ein throw std::runtime_error mit besagter Nachricht macht.



  • class MyException : public std::runtime_error
    {
    public:
        // Konstruktoren und was auch immer
    
        template <class T>
        MyException& operator <<(T&& thing) {
            m_message << std::forward<T>(thing);
            return *this;
        }
    
    private:
        std::stringstream m_message;
    };
    
    // ...
    
        throw MyException() << "blah" << "blub" << "blöbber" << 42 << wasAuchImmer;
    

    So weit so schlecht. Denn: Wie bekommt man diese Exception jetzt nothrow copyable? Und zwar ohne komisches Verhalten, also ohne dass sich z.B. die Kopien mit ändern wenn man das Original nach dem Kopieren ändert. Hmmmm...


Anmelden zum Antworten