Interface für generischen Typ



  • ccquestions schrieb:

    Inwiefern? Scheint mir die optimale Methode um veränderbare Settings, die ganz natürlicherweise unterschiedliche Typen haben können in einer gemeinsamen Klasse mit identischem Zugriff speichern zu können?
    Btw. Gäbs boost::any wohl kaum, wenn sowas nur benötigt wird, wenn beim Design was schief gelaufen ist..

    Das Problem ist, dass du auf verschiedene Typen keinen identischen Zugriff bekommst, sofern diese nicht von der selben Basisklasse erben, die das Interface bereitstellt - und in diesem Fall ist die ganze Apparatur eh unnötig.

    Überleg dir, wie der benutzende Code am Ende aussehen soll bzw. muss - ich habe Schwierigkeiten, mir ein anderes Vorgehen vorzustellen als "Versuch's mit dem Typ. Funzt nicht? Probier's mit dem nächsten.", und das ist alles andere als sauber.

    Wie man Konfigurationsdaten (wenn ich jetzt richtig verstehe, was du damit vorhast) am sinnvollsten verwaltet, hängt natürlich von der Applikation ab. Meiner Erfahrung nach - aber die ist nicht allumfassend - macht es wenig Sinn, die Konfigurationsdatei in verwursteter Form aber praktisch unverändert vorzuhalten; habe ich beispielsweise eine Einstellung "calculation_backend = foo", halte ich mir nicht "foo" in einem String vor, um später die damit verbundene Funktion rauszukramen, sondern setze gleich beim Einlesen der Konfiguration einen entsprechenden Funktionszeiger (oder wie auch immer ich das gerade verwalte). Jedenfalls unternehme ich alles sinnvoll mögliche, damit sich kein Stück Client-Code damit rumschlagen muss, den Typ einer Variable zu erraten. Selbst, wenn die Einstellungen zur Laufzeit änderbar sein sollen, macht ein Event-basierter Ansatz da für mein Verständnis mehr Sinn - bring es gleich in eine vernünftig maschinenlesbare Form, und reich die Verarbeitung von Einstellungen nicht nach außen an Code weiter, der sich mit anderen Problemen rumschlagen will.

    Ausnahmen gibt es im Wesentlichen da, wo man sich mit...naja, es läuft in gewisser Weise auf Laufzeitprogrammiersprachen raus. Nicht notwendigerweise so hochgestochen wie Script-Engines; ich denke da beispielsweise an Tabellenkalkulationen. Ich fluche zwar jedes mal über XLOPER, wenn ich an einem Excel-Plugin schreibe, aber ich sehe da tatsächlich keine sinnvolle Möglichkeit, das anders zu machen.



  • Nexus schrieb:

    ccquestions schrieb:

    Hab ich da nicht, sobald irgendwo mal der typeid header auftaucht im ganzen Programm das Runtime Type Tracing drin, was ja dann völlig idiotisch wäre, da ichs ja wirklich nur an der Stelle da benötige, notabene mit Typen, die bei der Kompilierung ja eigentlich dem Compiler sogar bekannt sind?

    Runtime type information. Das ist ohnehin standardmässig aktiviert (wegen Exceptions) und normalerweise kein Problem.

    Ist das wirklich so? Zumindest beim MSVC kann man Exceptions und RTTI getrennt von einander ein- und ausschalten. Mir ist auch nicht klar, was das Eine genau mit dem Anderen zu tun haben soll.



  • Tachyon schrieb:

    ...Exceptions und RTTI ... Mir ist auch nicht klar, was das Eine genau mit dem Anderen zu tun haben soll.

    Naja wie soll denn die Runtime checken ob ein Catch-Handler passt, wenn sie über die Typen nichts mehr weiss?



  • hustbaer schrieb:

    Tachyon schrieb:

    ...Exceptions und RTTI ... Mir ist auch nicht klar, was das Eine genau mit dem Anderen zu tun haben soll.

    Naja wie soll denn die Runtime checken ob ein Catch-Handler passt, wenn sie über die Typen nichts mehr weiss?

    Typischerweise schmeisst man keine Basisklasse und fängt dann abgeleitete Klassen. Und um von einer abgeleiteten Klasse auf die Basis zu kommen, braucht es kein RTTI. Die Basis einer abgeleiteten Klasse ist zur Compilzeit bekannt.



  • Wo ist eigentlich das Problem, eine (abstrakte) Basisklasse zu konstruieren, die nichts kann und von der alle weiteren abgeleitet sind.

    Dann kann man mit dynamic_cast einfach voll in die Basisklassen-Kiste reingreifen und wenn der cast funzt, hat man einen Treffer gelandet und sonst halt nicht.



  • Caster_Master schrieb:

    Wo ist eigentlich das Problem, eine (abstrakte) Basisklasse zu konstruieren, die nichts kann und von der alle weiteren abgeleitet sind.

    Dann kann man mit dynamic_cast einfach voll in die Basisklassen-Kiste reingreifen und wenn der cast funzt, hat man einen Treffer gelandet und sonst halt nicht.

    Ein Problem ist das nicht, aber wo ist der Sinn davon?



  • Ich hab manchmal das Gefühl, dass Java eine Generation von Programmierern herangezüchtet hat, die Objektorientierung für eine Eigenschaft der Sprache hält, in der ein Programm geschrieben ist. Es ist das Modell, Jungs! Nur, dass grep -r class src/ ein oder mehrere Zeilen zurückgibt, macht ein Programm noch nicht objektorientiert.

    OOP 101: Die Basisklasse enthält das Interface, das an Stellen benutzt werden soll, an denen sich der umgebende Code nicht mehr dafür interessiert, mit welcher konkreten Klasse er denn jetzt genau arbeitet. Ist dieses leer, kann kann der umgebende Code mit dem ihm übergebenen Objekt nichts machen, dementsprechend ist es nutzlos. Objektorientiert betrachtet ist "alles" nichts. Jetzt kann man natürlich Upcasts ohne Ende betreiben, und bei Übergangslösungen, die schnell gekittet werden müssen und später durch eine vernünftige Anpassung des Modells ersetzt werden, habe ich dafür auch Verständnis, aber man verlässt damit die Gefilde sauberer Objektorientierung. Wenn Client-Code eh rauskramen muss, womit er eigentlich gerade arbeitet, kannste genausogut einen void* und ein Flag durchreichen und ein großes Switch-Statement aufmachen.



  • seldon schrieb:

    OOP 101: Die Basisklasse enthält das Interface, das an Stellen benutzt werden soll, an denen sich der umgebende Code nicht mehr dafür interessiert, mit welcher konkreten Klasse er denn jetzt genau arbeitet. Ist dieses leer, kann kann der umgebende Code mit dem ihm übergebenen Objekt nichts machen, dementsprechend ist es nutzlos. Objektorientiert betrachtet ist "alles" nichts. ...

    Vorsicht! Jetzt kommen die Protype-OOP-Jungs und verhauen dich *gg*



  • Tachyon schrieb:

    hustbaer schrieb:

    Tachyon schrieb:

    ...Exceptions und RTTI ... Mir ist auch nicht klar, was das Eine genau mit dem Anderen zu tun haben soll.

    Naja wie soll denn die Runtime checken ob ein Catch-Handler passt, wenn sie über die Typen nichts mehr weiss?

    [...] um von einer abgeleiteten Klasse auf die Basis zu kommen, braucht es kein RTTI. Die Basis einer abgeleiteten Klasse ist zur Compilzeit bekannt.

    Beim Fangen von Ausnahmen sieht das etwas anders aus. Derjenige, der die Ausnahme schmeißt, weiß nicht unbedingt, wo sie gefangen wird und derjenige, der sie fängt, weiß nicht unbedingt, woher sie kommt. Da brauchst Du schon eine Art von Typinformation zur Laufzeit, um die richtige catch-Klausel zu finden und den Parameter zu initialisieren.



  • Klingt logisch. Nur wie kann ich dann einen C-String fangen? Ich mach gerne sowas in der Art throw "jetzt ist er umgefallen"; . Und das funktioniert. Nicht nur zufällig, weil wenn ich den catch-Block vergesse, schreibt er mir, dass const char * nicht gefangen wurde. Woher weiß also die Laufzeitumgebung, dass es sich um einen Zeiger auf char handelt? Oder wird das einfach angenommen?



  • Grad getestet auf codepad geht auch int. Also woher nimmt der Compiler hier die Info, wenn er für Klassen die RTTI braucht?



  • krümelkacker schrieb:

    [...]

    ISO/IEC 14882:2003 15.1 §3 schrieb:

    A throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”, respectively.[...]Except for these restrictions and the restrictions on type matching mentioned in 15.3, the operand of throw is treated exactly as a function argument in a call (5.2.2) or the operand of a return statement.



  • Mixxa schrieb:

    Also woher nimmt der Compiler hier die Info [...] ?

    Ist das nicht egal? Irgendwie wird das schon implementiert sein. Und typeid(int) funktioniert ja auch, ohne dass int eine (polymorphe) Klasse sein muss.



  • @ Tachyon:
    Das sagt nur etwas über den Typ des Exception-Objekts beim Werfen aus. Beim Fangen musst du aber je nach Typ dispatchen. Das geht nicht statisch, weil Exceptions erst zur Laufzeit fliegen.



  • Nexus schrieb:

    @ Tachyon:
    Das sagt nur etwas über den Typ des Exception-Objekts beim Werfen aus. Beim Fangen musst du aber je nach Typ dispatchen. Das geht nicht statisch, weil Exceptions erst zur Laufzeit fliegen.

    Ne, das geht genau so, wie der Aufruf von überladenen Funktionen.



  • Tachyon schrieb:

    Ne, das geht genau so, wie der Aufruf von überladenen Funktionen.

    Nein, Überladung wird zur Kompilierzeit aufgelöst.

    Welcher catch -Block aufgerufen wird, ist erst zur Laufzeit bekannt.



  • Nexus schrieb:

    Tachyon schrieb:

    Ne, das geht genau so, wie der Aufruf von überladenen Funktionen.

    Nein, Überladung wird zur Kompilierzeit aufgelöst.

    Welcher catch -Block aufgerufen wird, ist erst zur Laufzeit bekannt.

    Also mit den Compilern mit denen ich das hier gerade testen kann geht es auf jeden Fall ohne RTTI, und der Standard stimmt mir eigentlich auch zu. Ich meine allerdings, mich daran erinnern zu können, dass VS6 damit nicht klar gekommen ist.



  • Einfaches Beispiel:

    void Function()
    {
        if (rand() % 2 == 0)
            throw int(3);
        else
            throw double(7.2);
    }
    
    int main()
    {
       try
       {
            Function();
       }
       catch(int)
       {
            ...
       }
       catch(double)
       {
            ...
       }
    }
    

    Wie soll hier zur Kompilierzeit bestimmt werden, welcher catch -Block zum Zug kommt?



  • Genauso wie bei Polymophie und Überladung: Über eine interne Dispatch-Tabelle. Es ist doch alles zur Compilezeit bekannt. Sowohl die Handler, als auch die Exception als auch die throw-Operatoren und deren Positionen. Was soll man da mit RTTI rumdoktorn müssen?



  • Na indem der Compiler nur den geraden Zufall zuläßt. 😉
    Da muss man halt etwas kreativ sein. 😃


Anmelden zum Antworten