Über Datentypen interieren



  • Hallo. Wahrscheinlich habe ich nur einen Fehler im Ansatz. Ich versuche mal mein Problem zu beschreiben

    Die Idee ist, unterschiedliche Datentypen in einer Liste zu speichern und danach darüber zu iterieren.
    In der Schleife sollen zunächst allegemeine Dinge gemacht werden und dann eine Templatefunktion aufgerufen werden die für jeden einzelnen Datentypen spezialisiert ist

    ich verwende mal als pseudo code einen vector. Wohl wissentlich dass es nicht geht. Nur mal um das Problem zu verdeutlichen

    
    void Loop()
    {
       const std::vector sometypes = { TypeA,  TypeB };
    
       for (auto type : sometypes)
       {
            // Aus einer Liste Daten finden anhand des Namens
            auto dataX = GetDataFromList(typeid(T).name())
    
            auto dataY = GetDataDependsOn<type>()
    
            ...
       }
    }
    
    template<>
    GetDataDependsOn<TypeA>()
    {
       ...
    }
    
    template<>
    GetDataDependsOn<TypeB>()
    {
       ...
    }
    
    

    Kann mir jemand helfen wie ich das löse?


  • Mod

    Mit Laufzeitkonstrukten? Gar nicht. Zur Laufzeit gibt es keine Typen (außer vielleicht im weiteren Sinne a la typeid oder dynamic_cast…). Das müsstest du mittels Templatemetaprogrammierung machen. Sagt dir das etwas? Falls nein, dann hättest du erst einmal viel zu lernen, denn das ist nicht ganz einfach.

    Aber: Ich vermute mal, dass du das gar nicht brauchst. Was willst du wirklich erreichen? Das ist ja sicher kein Selbstzweck. Klingt so, als ob du vielleicht etwas in Richtung boost::Any suchst. Oder ein anderweitiges Designproblem hast.



  • @booster
    Schon mal std::variant angeschaut?



  • Hallo SeppJ

    Ja kann schon sein dass ich ein Designproblem habe. Sagte ja schon eventuell ist der Ansatz falsch.

    Ich weiß nicht ob ich das gesamte Problem so zusammenfassen kann dass es nicht zu viel Information ist und so dass man es noch versteht.

    Auf der einen Seite habe ich eine Klassenhierarchie das seine Daten serialisiert in eine Jsonstruktur .
    Ein anderer Teil der völlig unabhängig ist muss nun nochmals Daten aus selbiger Struktur raus suchen.

    Und dann diese Daten unterschiedlich je nach Typ (nicht alle in der Json Struktur nur ein paar) handeln.

    Klar könnte ich nun ein enum für die unterschiedlichen Fälle definieren und dann mit einem Switch case arbeiten.
    Da meine Enum Einträge aber ja eigentlich Typen entsprechen. Dachte ich kann mir das switch case sparen und für jede Typ halt eine spezielle Template Implementierung anbieten.

    Ohje ich weiß nicht ob man das versteht 😞



  • @Quiche-Lorraine
    Hi... ja daran habe ich auch gedacht
    Mir ist da aber ebenfalls nicht klar wie ich da drüber iteriere.

    Es gibt die std::visit. Aber wenn ich das richtig verstanden habe kann ich da nur für jeden Datentypen eine eigene Routine machen und die wird dann aufgerufen. Aber wie mache ich da einen allgemeinen Teil der für jeden Datentypen aufgerufen wird.



  • Über ein Variant kannst du nicht iterieren, aber über einen Vector von Variants. Und mit holds_alternative könnte man sogar dann zur Laufzeit nach Typen unterscheiden.

    Aber auch ein Visitor könnte eine Funktion aufrufen, die den Allgemeinen Teil abarbeitet. Gibt auf cpp Reference auch Beispiele, wie man mit std::visit arbeiten kann. https://en.cppreference.com/w/cpp/utility/variant/visit

    Du musst aber doch eh irgendwie durch das JSON Dokument durchgehen, oder? Vielleicht kannst du auch da einfach die entsprechenden Funktionen aufrufen, wenn du das jeweilige Objekt gefunden hast, dann musst du nicht irgendwie durch irgendeine Liste mit unterschiedlichen Typen iterieren, sondern kannst die Daten direkt so verarbeiten, wie sie vorliegen.



  • Schau dir mal das Video C++ Seasoning von Sean Parent an. Sein Anwendungsfall ist zwar ziemlich konkret, aber vllt lässt sich das auf deinen Fall anpassen.



  • @booster
    Ein Beispiel:

    #include <vector>
    #include <variant>
    #include <string>
    #include <iostream>
    
    using MySpecVariant = std::variant<int, double, std::string>;
    
    // KKDC: Kurzer Knackiger Demo Code
    int main() 
    {
        std::vector<MySpecVariant> L;
            
        L.push_back("Hallo");
        L.push_back(1);
        L.push_back(3.4);
        L.push_back("Welt");
        std::for_each(std::begin(L), std::end(L), [](const MySpecVariant& v) {
           if (std::holds_alternative<int>(v))
           {
               std::cout << "Integer: " << std::get<int>(v) << "\n";
           }
           else if (std::holds_alternative<double>(v))
           {
               std::cout << "Double: " << std::get<double>(v) << "\n";
           }
           else if (std::holds_alternative<std::string>(v))
           {
               std::cout << "String: " << std::get<std::string>(v) << "\n";
           }
        });
        return 0;
    }
    


  • @Quiche-Lorraine sagte in Über Datentypen interieren:

    Dazu muss ich dann aber für jeden Datentypen einen Wert anlegen. Da das bei mir aber keine rudimentären Datentypen sind sondern komplexe ist das mit dem Anlegen gar nicht so einfach. Des Weiteren sind manche Abstrakt und können gar nicht konstruiert werden.

    Ich möchte ja nur über die Datentypen iterieren.



  • @booster Irgendwie machst du da was komisches... Was soll den ein Datentyp ohne dazugehörige Instanz sein und was willst du davon abhängig machen?
    Für jeden Datentyp der in dem JSON File vorkommt? Oder, woher kommt der Datentyp? Wie hast du den "Typ" vorliegen?
    Mir ist gerade unklar was du genau warum machen möchtest. Vielleicht bin ich aber gerade einfach zu müde.



  • @booster
    Anhand deiner Beschreibung verstehe ich nicht wo du da über Datentypen iterieren wollen würdest.
    Das klingt eher danach als ob deine Klassen ein paar virtuelle Funktionen gebrauchen könnten.

    Oder vielleicht suchst du abstrakte Factories?
    Also sowas

    class MyAbstractFactory {
    public:
        virtual unique_ptr<CommonBaseClass> createFromJson(JsonThing const& json) = 0;
    };
    

    Und dann pro Datentyp eine konkrete Factory die von MyAbstractFactory abgeleitet ist.



  • Noch ein Versuch... In der Json stehen die Namen der Typen der Klasse als vollständiger Name drin.

    Vereinfacht

    class mynamspace::classA
    class mynamspace::classB
    class mynamspace::classC
    class mynamspace::classD
    class mynamspace::classE

    nun suche ich aus der Json den Typ classB und ClassD und möchte beide unterschiedlich behandeln

    Natürlich könnte ich nun ein vector machen der "classB" und "classD" als string enthält darüber iterieren (nur ein Beispiel mit den 2 Klassen) und dann mit einem switch case die beiden Typen unterschiedlich handeln.

    Aber ich dachte wieso als string speichern wenn ich die Datentypen speichern kann (bzw könnte) und dann nicht per switch case unterscheiden sondern per konkreter Implementierung.

    @hustbaer
    Ja das kann ich auch machen. Aber das löst ja nicht mein Problem. Ich möchte ja im Json nur nach bestimmten Datentypen suchen die darin abgelegt wurden.



  • Also mir gehts ja Hauptsächlich darum dass ich das switch case vermeide.
    Wenn ich mir nun in einem vector per typeid(classB).name den Namen der Klasse ablege,
    wie kann ich dann unterschiedliche Funktionen aufrufen. Je nachdem ob classB oder classD.



  • Naja, wie @SeppJ oben schon mal schrieb, zur Laufzeit gibt es keien Typen mehr....

    Wenn du kein Switch Case haben möchtest, wäre vlt ein Look Up Table für die nötigen Funktionen was für dich. Irgendwie was in dieser Art: std::map<std::string, std::function<void()>>

    #include <iostream>
    #include <string>
    #include <map>
    #include <functional>
    
    int main()
    {
      std::map<std::string, std::function<void()>> lut{
          {"classA", []() {std::cout << "class A \n"; }},
          {"classB", []() {std::cout << "class B \n"; }}
      };
      lut["classA"]();
      lut["classB"]();
    }
    

    Edit: Für typeid() braucht man doch auch eine Instanz des Typen den man nachgucken möchte, oder?



  • @Schlangenmensch

    Ja Lockuptabelle wäre auch eine Möglichkeit. Das andere hätte mir zwar besser gefallen.
    So muss ich nun für jeden Typen eine Funktion mit eigenem Namen erstellen und in die Lockuptabelle eintragen.

    Bei der anderen Lösung hätte ich dann immer den gleichen Namen der Funktion nur mit Templateparameter und ich müsste nicht immer noch die Lockuptabelle füllen. Aber wenns nicht geht, dann gehts nicht 🙂

    @Schlangenmensch sagte in Über Datentypen interieren:

    Edit: Für typeid() braucht man doch auch eine Instanz des Typen den man nachgucken möchte, oder?

    mit typeid hatte ich auch getestet. Aber die Info kann ich auch nicht in einem vector bzw Variable speichern.



  • @booster sagte in Über Datentypen interieren:

    Und dann diese Daten unterschiedlich je nach Typ (nicht alle in der Json Struktur nur ein paar) handeln.

    Das ist doch klassische Fall für Polymorphie. Wenn es nicht über virtuelle Funktionen gemacht wird, dann halt über Compilezeit Polymorphie. Man kann mit einer Typelist (Modern C++ Design sollte man mal gelesen haben, auch wenn es für C++98 ist) über die Typen iterieren.

    Als Beispiel kannst Du z.B. diesen Link nehmen.



  • @john-0 Ich sehe noch nicht, wie das hilft, wenn die "Typen" erst zur Laufzeit als String vorliegen. Das ist zumindest mein Verständnis, des Problems hier.

    Wenn die Typen natürlich schon zur Compiletime bekannt sind, dann go for it 😉



  • @john-0 sagte in Über Datentypen interieren:

    Das ist doch klassische Fall für Polymorphie.

    Im Grund ja. Natürlich kann ich die classB und classD von einer abstracten Basisklasse erben lassen die dann die Funktionen implementieren. Das ist ja das was auch Hustbear gemeint hat mit seiner Abstract Fatory. Wenn ich ihn richtig verstanden habe.

    @Schlangenmensch sagte in Über Datentypen interieren:

    Wenn die Typen natürlich schon zur Compiletime bekannt sind, dann go for it

    Ja du hast es erkannt daran liegt das Problem. Die Typen sind erst zur Laufzeit bekannt.



  • @booster sagte in Über Datentypen interieren:

    Ja du hast es erkannt daran liegt das Problem. Die Typen sind erst zur Laufzeit bekannt.

    Naja, Du beschreibst das äußerst umständlich. Vor allem wenn Du Dinge serialisieren willst, liegen sie als Objekte im Speicher vor. D.h. die Typen sind zur Compilezeit bekannt. Üblicherweise hat man in C++ das Problem beim umgekehrten Schritt bei der Deserialisierung sprich man bekommt einen Strom an Text und will daraus wieder Objekte bauen. Letzteres ist als Reflection bekannt.



  • @john-0 sagte in Über Datentypen interieren:

    Naja, Du beschreibst das äußerst umständlich.

    Wenn du meinst.

    Das Problem scheint noch nicht verstanden zu sein. Aus dem Text Objekte zu bauen ist auch nicht das Problem.

    Nochmals. Wir machen das etwas abstrakter.

    Wir haben eine Liste.

    EintragA
    EintragB
    EintragC
    EintragD
    EintragA
    EintragB
    EintragC
    EintragD

    Nun möchte ich aus der Liste 2 Einträge raus suchen und unterschiedlich behandeln
    Also halte ich mir eine zweite Liste in der ich sage welche Einträge ich suchen möchte.

    Iteriere über beide Listen drüber und vergleiche und wenn ich den passenden Eintrag in der Liste gefunden habe muss ich etwas bestimmtes machen mit dem Eintrag.

    So weit so gut. Die Idee war nun da die Einträge einem Datentypen entsprechen, also dessen Namen, ich keine Liste mit Namen speichere sondern eine Liste mit Datentypen.

    Die Behandlung der beiden unterschiedlichen Einträge wäre dann durch Polymorphie oder Templateprogrammierung möglich.

    Immer noch unverständlich. Wenn ja. Dann ists halt so. Sorry.


Anmelden zum Antworten