#include <print>
#include <iostream>

// Definition der Ausnahmeklasse
class Exception
{
    short errNum; // Fehlernummer
    inline static const char* pErrText[] = // Fehlertexte
    {
        "Division durch 0",
        "Wurzel aus negativer Zahl"
    };
public:
    enum eMathErr {ZDIV, SQRT}; // Fehlernummern
    explicit Exception(eMathErr err);
    Exception(const Exception& src);
    ~Exception();
    friend std::ostream& operator << (std::ostream& os,
                               const Exception& ex);
};

// Definition der Memberfunktionen
// Konstruktor
inline Exception::Exception(eMathErr err)
{
    errNum = err;
    std::println("ctor: {}", reinterpret_cast<void*>(this));
}
// Hinweis: ohne Ausgabe koennte der copy-ctor
// auch als default copy-ctor definiert werden
inline Exception::Exception(const Exception& src)
{
    errNum = src.errNum;
    std::println("copy-ctor: {}", reinterpret_cast<void*>(this));
}
// Destruktor
inline Exception::~Exception()
{
    std::println("dtor: {}", reinterpret_cast<void*>(this));
}
// Überladener Ausgabeoperator
std::ostream& operator << (std::ostream& os,
                           const Exception& ex)
{
    std::println(os,"Fehler {:s} aufgetreten!", ex.pErrText[ex.errNum]);
    return os;
}

// Testfunktion für Division
void Div(int val)
{
    // Falls Divisior 0, dann Ausnahme auslösen
    if (val == 0)
        throw Exception(Exception::ZDIV);
    std::cout << "Division erlaubt!\n";
}

// Testfunktion für Wurzelberechnung
void Sqrt(double val)
{
    // Falls Wurzelwert negativ, dann Ausnahme auslösen
    if (val < 0.0)
        throw Exception(Exception::SQRT);
    std::cout << "Wurzel kann berechnet werden!\n";
}

// main() Funktion
int main()
{
    // Ausnahmebehandlung einleiten
    try
    {
        Div(1); // Das geht fehlerfrei
        Div(0); // Führt zum Auslösen einer Ausnahme
    }
    // Ausnahme abfangen
    catch(const Exception& ex)
    {
        std::cout << ex; // Fehlermeldung ausgeben
    }
    // Default-Behandlung für Exceptions
    catch(...)
    {
        std::cout << "unbekannte Ausnahme!\n";
    }
    // Neue Ausnahmebehandlung einleiten
    try
    {
        Sqrt(1.2); // Das geht fehlerfrei
        Sqrt(-1.2); // Führt zum Auslösen einer Ausnahme
    }
    // Ausnahme abfangen
    catch(const Exception& ex)
    {
        std::cout << ex;
    }
    // Default-Behandlung für Exceptions
    catch(...)
    {
        std::cout << "unbekannte Ausnahme!\n";
    }

    // Fertig
    std::cout << "Fertig!\n";
}
