#include <iostream>
#include <stdexcept>
#include <typeinfo>
#include <print>

// Globale Konstante für max. Parameterwert des Any ctors
constexpr auto ULIMIT=5;

// Klasse löst im ctor eine out_of_range Exception aus
class Any
{
  public:
    explicit Any(int param)
    {
        if ((param<0) || (param>=ULIMIT))
            throw std::out_of_range("ctor-Parameter <0 oder >=5 !\n");
        std::cout << "ctor Any\n";
    };
    virtual ~Any()
    { std::cout << "dtor Any\n"; };
};

// Klasse löst in Add() eine underflow_error oder overflow_error Exception aus
// wenn Ergebnis der Addition ausserhalb des short Wertebereichs liegt
class Another
{
  public:
    Another()
    { std::cout << "ctor Another\n"; };
    virtual ~Another()
    { std::cout << "dtor Another\n"; };
    short Add(short op1, short op2) const
    {
        short res = op1 + op2;
        if ((op1<0) && (op2<0) && (res>0))
            throw std::underflow_error("Underflow in Another::Compute()\n");
        if ((op1>0) && (op2>0) && (res<0))
            throw std::overflow_error("Overflow in Another::Compute()\n");
        return res;
    }
};

// main() Funktion
int main()
{
    std::println("Programm-Start");
    // 5 Any Objekte nacheinander anlegen und wieder löschen
    Any *pAny = nullptr;
    for (auto i=0; i<=ULIMIT; i++)
    {
        // Versucht Any Objekt anzulegen
        // Max. ctor Parameterwert muss kleiner ULIMIT sein
        try
        {
            pAny = new Any(i);
        }
        catch(const std::out_of_range& ex)
        {
            std::cout << ex.what();
            pAny = nullptr;
        }
        catch (...)
        {
            std::cout << "*** Unbekannte Ausnahme: new ***\n";
            pAny = nullptr;
        }
        // Typ des derefenzierten pAny Zeigers bestimmen
        try
        {
            std::cout << "Type of Any: " << typeid(*pAny).name() << '\n';
        }
        catch (const std::bad_typeid& ex)
        {
            std::cout << ex.what() << '\n';
        }
        catch (...)
        {
            std::cout << "*** Unbekannte Ausnahme: typeid ***\n";
        }
        // Any Objekt wieder löschen
        delete pAny;
    }

    // Neues Any Objekt in Another Objekt konvertieren
    try
    {
        pAny = new Any(4);
        Another& ref = dynamic_cast<Another&>(*pAny);
        ref.Add(10,10);
    }
    catch (const std::bad_cast& ex)
    {
        std::cout << ex.what() << '\n';
    }
    catch (...)
    {
        std::cout << "*** Unbekannte Ausnahme: dynamic_cast ***\n";
    }
    // Any Objekt wieder löschen
    delete pAny;

    // Another Objekt definieren und verschiedene Additionen durchführen
    Another obj;
    try
    {
        std::cout << "Addition: " << obj.Add(20000,10000) << '\n';
        std::cout << "Addititon " << obj.Add(-20000, 30000) << '\n';
        std::cout << "Addititon " << obj.Add(20000, 30000) << '\n';
    }
    catch (const std::overflow_error& ex)
    {
        std::cout << ex.what();
    }
    catch (const std::underflow_error& ex)
    {
        std::cout << ex.what();
    }
    catch (...)
    {
        std::cout << "*** Unbekannte Ausnahme: Add() ***\n";
    }
}
