Organisation von Speichern und Laden



  • Hi,
    ich habe für eine Klasse jeweils eine Funktion zum Speichern und zum Laden der Daten der Klasse aus bzw. in eine XML-Datei.

    Wie ist da die beste Möglichkeit zur Aufteilung? Die Funktionen als Member wie bisher oder als Globale Funktion?


  • Mod

    Da diese Funktionen doch wohl höchstwahrscheinlich Streamoperatoren sein dürften (oder sein sollten, wenn sie es bisher nicht sind), hast du doch gar keine andere Möglichkeit, als diese als freie Funktionen zu implementieren. Schließlich muss der Stream das erste Argument der Funktion sein, diese kann folglich keine Memberfunktion deiner Klasse sein.

    Da Funktionen dieser Art oft auch auf interne Datenfelder einer Klasse zugreifen möchten, werden sie normalerweise entweder als friend der Klasse deklariert, oder sie rufen dann noch einmal eine zusätzliche Lese- bzw. Schreibfunktion auf, die als public-Member der Klasse implementiert wird.



  • SeppJ schrieb:

    Da diese Funktionen doch wohl höchstwahrscheinlich Streamoperatoren sein dürften

    Aktuell nicht, da ich lesen bzw. schreiben über boost::property_tree::ptree::write_xml() mache.



    1. Interface style
    struct IXmlSerializable // name after your taste
    {
    	virtual void serialize(/*...*/) = 0; // name after your taste
    	virtual void parse(/*...*/) = 0; // name after your taste
    	virtual ~IXmlSerializable() = default;
    };
    
    class MeineDaten : public IXmlSerializable
    {
    	void serialize(/*...*/) override; // ...
    	void parse(/*...*/) override; // ...
    };
    
    1. Der Weg über boost::property_tree schließt operator<< / operator>> nicht aus.

    2. Oder du machst es wie ich mit Introspection über boost::hana oder boost::fusion, aber das ist aufwendiger. (BOOST_FUSION_ADAPT_STRUCT, BOOST_HANA_ADAPT_STRUCT)
      EDIT: Weil hier entfällt das schreiben der Umwandlungen komplett, einmal entwickelt muss ich nie wieder was machen, weil alles generiert wird.



  • 5cript schrieb:

    1. Interface style

    Die Idee hatte ich auch schon, nur bleibt bei mir im Kopf dann immer das Problem das ich eine Vererbungshierarchie habe in der sowohl die Basis als auch die abgeleitete Klasse unterschiedliche Dateiausgaben produzieren.
    Die Ausgabe an sich ist auch nicht das Problem, sondern eher das Einlesen, denn beim Einlesen müsste eine Differenzierung stattfinden zu welchem Datentyp die Datei gehört. Das ist es was mir da schwierigkeiten bereitet.
    Deswegen war meine Überlegung einen Parser zu schreiben der eben diese Differenzierung beherscht. Nur weiß ich nicht so recht wie ich diesen schreiben soll, ohne für jeden Typ der hinzukommt, den Parser umbauen zu müssen.



  • Interfacestyle find ich nicht gut. Da müssen die Daten entscheiden, wie sie sich organisieren möchten, das ist nicht deren Aufgabe.
    Ist der Zugriff auf alle Daten über ein öffentliches Interface zugänglich? Falls ja würde ich das Laden/Speichern als freie Funktion implementieren.



  • Die Ausgabe an sich ist auch nicht das Problem, sondern eher das Einlesen, denn beim Einlesen müsste eine Differenzierung stattfinden zu welchem Datentyp die Datei gehört. Das ist es was mir da schwierigkeiten bereitet.

    Das ist ein ganz anderes Level an Schwierigkeit, danach hattest du nicht gefragt.
    Kannst du im XML ein Knoten einfügen, der das entscheidet?
    Oder haben die root Knoten wenigstens einen einzigartigen Namen?

    Ich würde das dann trotzdem mit einem Vererbungsdesign machen und in Abhängigkeit von Daten in der XML entscheiden welches Objekt tatsächlich dynamisch erstellt wird, was sich dann selbst lesen kann.
    "Ich würde" ist relativ, weil ich es eigentlich für mich komplett anders gelöst habe.

    EDIT - Tipp: Da boost::property_tree eine schöne Baumstruktur aufbaut eignen sich rekursive algorithmen besonders gut.



  • DocShoe schrieb:

    Ist der Zugriff auf alle Daten über ein öffentliches Interface zugänglich?

    Ja der Zugriff ist möglich.

    5cript schrieb:

    Kannst du im XML ein Knoten einfügen, der das entscheidet?
    Oder haben die root Knoten wenigstens einen einzigartigen Namen?

    Ja könnte ich. Ich habe dabei keinerlei Einschränkungen. Ich bin nicht mal an XML gebunden. Theoretisch könnte ich auch was in die Richtung SQLite machen und das als Datei nutzen.

    5cript schrieb:

    "Ich würde" ist relativ, weil ich es eigentlich für mich komplett anders gelöst habe.

    Was wäre denn deine Herangehensweiße? Ich bin wie gesagt vollkommen frei, es muss nur funktionieren.

    @5cript: Hättest du mal ein funktionierendes Beispiel für Datei Ein- und Ausgaben mit boost::hana oder boost::fusion? Damit ich auch diese Möglichkeit mal abschätzten kann.



  • https://github.com/5cript/SimpleJSON

    und (wie man über eine Klasse rüber iteriert).
    https://github.com/5cript/SimpleJSON/blob/master/stringify/jss_fusion_adapted_struct.hpp#L58-L117

    Die benutze ich überall (ich nehm immer JSON für alles). Hab auch eine undokumentierte XML variante, aber da musste ich für attribute etc etwas tricksen und die kann nicht so viel.

    Außerdem muss ich hier noch dokumentieren, wie man eigene Serialisierungen implementiert, falls man eine Klasse nicht "adapten" kann.
    Und die lib dateien haben hässliches prefixing, aber das kann ich jetzt nicht mehr so einfach rausnehmen, weil ich sonst damit all meine projekte zerschieße XD.



  • Wenn ich das richtig verstanden habe, muss ich die Klassen die ich ausgeben will von JSON::Stringifiable und JSON::Parsable erben lassen?

    Oder kann ich da eine da eine Adapter-Klasse erstellen bei der ich wie in deinem Beispiel someContainer durch mein Objekt austausche?

    struct ConfigContent : public JSON::Stringifiable <ConfigContent>
                         , public JSON::Parsable <ConfigContent>
    {
        int id;
        std::string libPath;
        std::vector <std::string> someContainer; <- Hier würde dann meine eigene Klasse stehen
    };
    


  • Damit in meiner lib ein Objekt serialisierbar/parsbar wird müssen alle member serialisierbar/parsbar sein, die in BOOST_FUSION_ADAPT_STRUCT sind (logisch, da der rest unsichtbar ist) und die Methode "stringify" oder "parse" verfügbar sein.

    Ein serialisierbares Objekt ist entweder was aus der STL (zumindest fast alles, strings, vector usw.), ein Fundamentaltyp, oder eine adaptierte Klasse mit stringify/parse methode. Du merkst: Die Definition ist rekursiv. Man kann also schachteln wie man lustig ist.

    Die Ableitung bietet die Standardimplementierung für eine stringify member function an.
    https://github.com/5cript/SimpleJSON/blob/master/stringify/jss_fusion_adapted_struct.hpp#L102-L108
    Die kann man auch einfach selber anbieten, weil ich hier keine polymorphie benutze, sondern statisch nach einer Funktion mit der Signatur suche (SFINAE Magie). Die Ableitung ist also optional.

    EDIT: Ein Adapter gibt es nicht, aber jetzt wo du es sagst, bau ich das wohl mal ein. Das ist sogar ziemlich einfach einzubauen.


Log in to reply