Schickes Interface für Strukturierte Daten gesucht
-
Hi zusammen
ich baue aktuell ein Modul zum Schreiben von strukturierten Daten und bin unentschlossen, was das Interface angeht.
Das interface soll möglichst intuitiv sein und die strukturierung von Daten ermöglichen, im Stile von XML z.b.Variante 1 StreamOperatoren
DatenObjekt obj; obj << StartGruppe(1) << Element1 << Element2 << Element3 << EndeGruppe() << Element5 << Element6;
Variante 2 Klassisch
DatenObjekt obj; obj.startGruppe( 1 ); obj.write( Element1 ); obj.write( Element2 ); obj.write( Element3 ); obj.endeGruppe(); obj.write( Element5 ); obj.write( Element6 );
Variante 3 Parameter Pack
DatenObjekt obj; obj.writeGruppe( 1, Element1, Element2, Element3 ); obj.writeElement( Element5, Element6 );
In meinem Fall sind es dann allerdings keine einzelnen Elemente sondern Key-Value-Paare. Das soll aber zunächst nicht von Bedeutung sein.
Bevorzugt ihr eine der Varianten, oder habt ihr da noch andere Ideen, wie man solch ein Interface umsetzen kann?
-
Kann Boost.PropertyTree was Du brauchst?
-
Was spricht gegen
Datengruppe g; DatenObjekt o; g << element1 << element2 << element3 << element4 o << g;
-
@DocShoe sagte in Schickes Interface für Strukturierte Daten gesucht:
Was spricht gegen
Datengruppe g; DatenObjekt o; g << element1 << element2 << element3 << element4 o << g;
Nix Sowas ähnliches kam mir auch schon in den Sinn.
Wobei das analog auch mit ParameterPacks geht, wenn man sich für die DatenGruppe eine create-Funktion baut.
DatenGruppe g = gruppe( 1, Element1, Element2, Element3 ); DatenObjekt o; o.write( g, Element5, Element6 );
so in der Art jedenfalls.
Mir ist halt wichtig, dass es einen halbwegs modernen Eindruck hinterlässt. Vermutlich werde ich die klassische Variante, wie auch die meisten XML-Schreiber arbeiten, aus kompatibilitäts-Gründen ohnehin anbieten müssen, aber ich will eben auch eine etwas elegantere Variante zusätzlich anbieten, die vielleicht auch weniger Schreibaufwand ist.
-
@Swordfish sagte in Schickes Interface für Strukturierte Daten gesucht:
Boost.PropertyTree
Danke für den Hinweis. Muss ich mir erstmal anschauen. Sagt mir bisher gar nix
-
Sowas ist hier wäre definitiv machbar (finde ich ne ganz nette Syntax):
auto d = dokument{ gruppe{ element{ "element1" }, element{ "element2" }, element{ "element3" } }, gruppe{ gruppe{ element{ "element4" }, element{ "element5" }, element{ "element6" } } } }; d.append( gruppe{ element{ "element7" } }, ); std::cout << d.child(1) << "\n"; <gruppe> <gruppe> <element>element4</element> <element>element5</element> <element>element6</element> </gruppe> </gruppe> std::cout << d.child(1).child(0).child(2).value() << "\n"; element6
Ich habe zwar jetzt nicht die Muße, das in Code auszuformulieren, aber ich sehe nicht, warum sowas nicht ginge.
-
@Finnegan
Meinst du die geschweiften Klammern in Bezug auf intialisierungslisten?Und das müsste auf die Art eigentlich sogar halbwegs performant sein, wenn die Konstruktoren mit { .... } komplett zur Compilezeit aufgelöst werden. Oder?
(unabhängig mal von der Art der Speicherung im Hintergrund )
-
@It0101 sagte in Schickes Interface für Strukturierte Daten gesucht:
Und das müsste auf die Art eigentlich sogar halbwegs performant sein, wenn die Konstruktoren mit { .... } komplett zur Compilezeit aufgelöst werden. Oder?
initializer_list
s und performance passen nicht unbedingt zusammen: The Cost of std::initializer_list ... aber Du meinst mit variadic templates?Ich kann mir aber nicht vorstellen daß das bei solch einer Datenstruktur von Bedeutung ist.
-
@It0101 @Swordfish Das geht auch ohne
std::initializer_list
, ich hab hier mal schnell was zusammengestoppelt, das die Idee hoffentlich etwas illustriert (nicht perfekt, nur ein erster grober Entwurf):Ideone-Demo: https://ideone.com/AnjdAc
#include <iostream> #include <utility> #include <memory> #include <vector> #include <stdexcept> class element { public: element() = default; virtual ~element() = default; element(std::string value) : value_{ std::move(value) } { } auto value() const -> const std::string& { return value_; } virtual const element& child(std::size_t i) const { throw std::runtime_error("Invalid child index."); } virtual void print(std::ostream& out, int level) const { out << std::string(level * 2, ' ') << "<element>" << value() << "</element>"; } private: std::string value_; }; auto operator<<(std::ostream& out, const element& e) -> std::ostream& { e.print(out, 0); return out; } class group : public element { public: template <typename... Children> group(Children&&... children) { append(std::forward<Children>(children)...); } template <typename... Children> void append(Children&&... children) { ( children_.emplace_back( std::make_unique<Children>(std::forward<Children>(children)) ), ... ); } virtual const element& child(std::size_t i) const override { if (i >= children_.size()) throw std::runtime_error("Invalid child index."); return *children_[i]; } virtual void print(std::ostream& out, int level) const override { out << std::string(level * 2, ' ') << "<group>\n"; print_children(out, level + 1); out << std::string(level * 2, ' ') << "</group>"; } void print_children(std::ostream& out, int level) const { for (const auto& child : children_) { child->print(out, level); out << "\n"; } } private: std::vector<std::unique_ptr<element>> children_; }; class document : public group { public: template <typename... Children> document(Children&&... children) : group{ std::forward<Children>(children)... } { } protected: virtual void print(std::ostream& out, int level) const override { out << std::string(level * 2, ' ') << "<document>\n"; print_children(out, level + 1); out << std::string(level * 2, ' ') << "</document>"; } }; auto main() -> int { auto d = document{ group{ element{ "element1" }, element{ "element2" }, element{ "element3" } }, group{ group{ element{ "element4" }, element{ "element5" }, element{ "element6" } }, element{ "element7" } } }; d.append( group{ element{ "element8" } } ); std::cout << "\nd --------------------------------------\n\n" << d << "\n"; std::cout << "\nd.child(1) -----------------------------\n\n" << d.child(1) << "\n"; std::cout << "\nd.child(1).child(0).child(2).value() ---\n\n" << d.child(1).child(0).child(2).value() << "\n\n"; }
Das ist noch ausbaufähig und hat auch noch ein paar Probleme. Z.B. wird bei einem Element, das ein einziges Element seines eigenen Typs enthalten soll der Copy-Konstruktor aufgerufen:
group { group { ... } }
kopiert die innere Gruppe in die äußere. Im Kopier-Kontext ist das ja durchaus korrekt, in diesem Fall jedoch nicht. Interessantes Problem, für das ich auf die Schnelle noch keine elegante Lösung habe.Auch habe ich die
child()
-Methode bereits imelement
-Basisobjekt deklariert - auch wenn nurgroup
-Objekte Kinder haben können, und auchgroup
unddocument
erben dievalue()
-Methode. Alles um diesesd.child(1).child(0).child(2).value()
zu ermöglichen. Vielleicht will man das etwas anders designen - oder vielleicht ist es ja auch nicht so schlecht - wie gesagt, nur ein erster EntwurfP.S.: Noch ne Idee für später: Wenn die Anzahl der Element-Typen überschaubar bleibt, dann könnte man die vielleicht auch in einem
std::variant
speichern, statt mitstd::unique_ptr
zu arbeiten. Das würde die Datenstruktur im Speicher noch etwas kompakter und eventuell auch etwas effizienter machen (std::vector
undstd::string
haben dann zwar immer noch dynamischen Speicher, aber man reduziert es ein wenig). Ich entwerfe Datenstrukturen gerne so, dass sie nicht übermäßig "all over the place" im Speicher liegen.
-
@Finnegan sagte in Schickes Interface für Strukturierte Daten gesucht:
das die Idee hoffentlich etwas illustriert
@Swordfish sagte in Schickes Interface für Strukturierte Daten gesucht:
aber Du meinst mit variadic templates?
-
@Swordfish sagte in Schickes Interface für Strukturierte Daten gesucht:
@Swordfish sagte in Schickes Interface für Strukturierte Daten gesucht:
aber Du meinst mit variadic templates?
Ich weiss nicht ob ich dich richtig verstehe. Willst du eher auf etwas ohne virtuelle Vererbung hinaus, wo die Daten z.B. in einer geschachtelten
std::tuple
-Struktur oder etwas ähnlichem gespeichert sind? ich die Richtung geht bestimmt auch was, das am Ende ähnlich zu benutzen ist, das wird aber definitiv pfriemeliger - nix für "mal eben zusammenstoppeln"
-
@Finnegan sagte in Schickes Interface für Strukturierte Daten gesucht:
Willst du eher auf etwas ohne virtuelle Vererbung hinaus
Nene, sowas in der Art hab' ich mit variadic template schon gemeint.
-
@Finnegan danke, dass du soviel Zeit investiert hast. Das wäre wirklich nicht nötig gewesen