boost::variant als Dispatcher



  • Sagen wir mal, wir haben bei uns im System eine Schnittstelle IIrgendwas mit paar Ableitungen. Jetzt muss ich irgendwas mit diesen Ableitungen machen, was die Basisschnittstelle nicht bietet. Ich brauche also einen Visitor, den es in der Basisschnittstelle nicht gibt und den ich nicht einbauen will.
    Ich denke da spontan an

    boost::variant<Derived1, Derived2, Derived3> v;
    v = base;

    Und einen entsprechenden boost::static_visitor. Hab damit aber kaum mal was gemacht. Funktioniert es überhaupt, wenn man eine Basisklasse reinsteckt? Macht es Sinn, gibt es etwas leichtgewichtigeres?



  • Mechanics schrieb:

    Sagen wir mal, wir haben bei uns im System eine Schnittstelle IIrgendwas mit paar Ableitungen. Jetzt muss ich irgendwas mit diesen Ableitungen machen, was die Basisschnittstelle nicht bietet.

    Das hört sich nach dynamic_cast oder typeid an. variant macht etwas völlig anderes und hat gar nichts mit virtual zu tun.

    Vielleicht X/Y-Problem?



  • Mechanics schrieb:

    Funktioniert es überhaupt, wenn man eine Basisklasse reinsteckt?

    Nein, natürlich nicht.

    gibt es etwas leichtgewichtigeres?

    Schnittstelle erweitern?



  • Ja, hab mir mittlerweile schon gedacht, dass es nicht funktioniert. Und dann sogar in den Code geschaut...

    Die Schnittstelle will ich nicht erweitern. Was ich mit den Teilen machen muss ist ein Sonderfall, das will ich nicht in die Basisschnittstelle aufnehmen. Wär auch schwierig, es gibt Implementierungen in mehreren unterschiedlichen Dlls, da kann man nicht einfach einen normalen Visitor definieren. Und ich muss die Objekte schon sehr konkret kennen (ich schreib an der Stelle sozusagen high level Code, ich kenne tatsächlich alle Implementierungen, die mich interessieren), da reicht nicht einfach eine allgemeine virtuelle Methode.
    Mit dynamic_cast könnte ich an der Stelle schon leben, hab ich als ersten schnellen Prototypen auch schon drin. Gefällt mir aber nicht, deswegen suche ich etwas eleganteres.



  • Dann beschreib mal etwas genauer was du machen willst und um was für Objekte es sich handelt.

    So allgemein kann man dazu nicht viel mehr sagen.



  • Ganz genau kann/will ich das jetzt auch nicht beschreiben... Es sind im Endeffekt Prozesse im CAD Bereich, z.B. parametrische Modelle generieren und mit Daten anreichern oder Parameter abhängig von irgendwas machen. Die polymorphen Objekte, um die es mir geht, sind "Filter", die am Prozess beteiligt sind. Die können aus verschiedenen Quellen kommen und reingehängt werden. Die Schnittstelle an sich ist recht schmal, das sind 3-4 Funktionen, das soll auch so bleiben. Dahinter können aber viel komplexere Objekte stecken, das können z.B. wieder Listen von Regeln sein, nach denen Parameter modifiziert werden, oder Formeln, oder Scripte... Nichts, was man in eine schmale Reflection-Schnittstelle packen könnte.
    Was ich schreibe ist eine Visualisierung des Prozesses als Graph. Und an der Stelle wollen wir für die bekannten Filter mehr Informationen anbieten und teilweise auch eine Möglichkeit haben, die zu konfigurieren. D.h. ich muss die eigentlichen Objekte hier konkret kennen und tu das auch. Ich will aber nichts ala "createEditWidget" und "getPropertyDescriptors" in die Schnittstelle einbauen, das würde sie viel komplizierter machen und die wird auch extern verwendet.
    Ich muss also in meinem Code paar Fälle unterscheiden, z.B. ist es jetzt ein "RegExpAttributeFilter", dann hole ich mir von dem die Liste von Ausdrücken und zeig die an. Mit paar dynamic_casts könnte ich wie gesagt schon leben, aber ich würd das gern zumindest irgendwie verstecken.



  • Ich würde sagen das ist eh schon konkret genug 🙂

    Mach doch eigene Interfaces.
    Jeder Filter kann dann, muss aber nicht, das IFilterGraphEditorStuff Interface (dir fällt sicher ein besserer Name ein) implementieren.
    Und du machst einfach einen einzigen dynamic_cast auf IFilterGraphEditorStuff* .

    Damit sparst du dir alle konkreten Klassen mit dynamic_cast durchzuprobieren, musst aber gleichzeitig das primäre Filter Interface nicht aufbohren.
    Und du kannst das Interface über das dein Graph Editor mit den bekannten Klassen kommuniziert so schön oder so hässlich machen wie du willst -- du musst es dann ja nur noch für die paar Klassen implementieren die es auch wirklich unterstützen.

    Und falls dir das immer noch zu kompliziert ist...

    Es gibt wohl grundsätzlich nur die zwei Möglichkeiten.
    Entweder du hast ne starke Dependency zwischen dem Editor und den konkreten Implementierungen der Filterklassen. D.h. der Editor muss exakt zu den Filterklassen passen, sonst kracht es. Das Durchprobieren mit dynamic_cast ist dabei dann wohl das geringste Problem, d.h. dann kannst du das auch gleich so lassen.

    Oder, wenn du das nicht willst, musst du eine Abstraktionsschicht einbauen. Also das was du mit "createEditWidget" und "getPropertyDescriptors" beschrieben hast -- wobei man da natürlich auch einiges an Freiraum auf der konkret<->abstrakt Achse hat.



  • Offenbar noch nicht konkret genug 😉
    Eine zusätzliche Schnittstelle für Editoren ist sicher keine schlechte Idee. Ein Problem ist hier aber, dass einige der interessanten Filter in Low Level Dlls sind, die keine Abhängigkeiten zur GUI haben. Ist ein Nebenaspekt, den ich unterschlagen habe.
    Das andere ist, wenn ich sowas wie "getPropertyDescriptors" einführe, wirds natürlich kompliziert, weil ich dann tatsächlich eine Infrastruktur brauche, um die irgendwie deklarativ zu beschreiben. Oder ich mache da eine plumpere Funktion wie "paintNode" rein, und die können einfach irgendwas draufzeichnen. Das gefällt mir jetzt irgendwie nicht so wirklich...
    "Kompliziert" ist natürlich auch ein Argument. Kompliziert ist vielleicht nicht ganz das richtige Wort, man muss das Kosten-Nutzen Verhältnis abwägen. Wir einfach schon so viel "fertigen Code", dass ich das ganze ziemlich schnell fertigstellen konnte. Wenn ich jetzt anfange, das alles aufzubohren und umzubauen, werd ich wahrscheinlich 2-3 mal länger brauchen, mit dem einzigen "Vorteil", dass ein paar dynamic_casts rausfliegen.

    Ok, ich werds dann wohl dabei belassen. Werd mir nebenbei aber denk ich noch überlegen, wie man so eine optionale Editor Schnittstelle sinnvoll definieren könnte, vielleicht wollen auch mal externe Entwickler ihre Filter entsprechend anpassbar machen...



  • Mechanics schrieb:

    Offenbar noch nicht konkret genug 😉

    Doch doch, konkret genug 😉

    Mechanics schrieb:

    Eine zusätzliche Schnittstelle für Editoren ist sicher keine schlechte Idee. Ein Problem ist hier aber, dass einige der interessanten Filter in Low Level Dlls sind, die keine Abhängigkeiten zur GUI haben. Ist ein Nebenaspekt, den ich unterschlagen habe.

    Ist aber auch wurscht.
    Das Interface muss ja nicht in der GUI definiert sein -- die GUI verwendet es bloss. Definiere es auf der Ebene wo auch das Filter-Interface definiert ist.

    Mechanics schrieb:

    Das andere ist, wenn ich sowas wie "getPropertyDescriptors" einführe, wirds natürlich kompliziert, weil ich dann tatsächlich eine Infrastruktur brauche, um die irgendwie deklarativ zu beschreiben. Oder ich mache da eine plumpere Funktion wie "paintNode" rein, und die können einfach irgendwas draufzeichnen. Das gefällt mir jetzt irgendwie nicht so wirklich...

    Naja wie gesagt, gibt viele Möglichkeiten. Muss ja nicht "paintNode" sein. Könnte z.B. eine getDescription() Funktion sein die nen HTML-Schnippel zurückliefert.

    Mechanics schrieb:

    "Kompliziert" ist natürlich auch ein Argument. Kompliziert ist vielleicht nicht ganz das richtige Wort, man muss das Kosten-Nutzen Verhältnis abwägen. Wir einfach schon so viel "fertigen Code", dass ich das ganze ziemlich schnell fertigstellen konnte. Wenn ich jetzt anfange, das alles aufzubohren und umzubauen, werd ich wahrscheinlich 2-3 mal länger brauchen, mit dem einzigen "Vorteil", dass ein paar dynamic_casts rausfliegen.

    Der grosse Vorteil den ich da sehe ist nicht dass ein paar dynamic_cast rausfliegen. Sondern dass du "harte", unbequeme Abhängigkeiten auf die "Innereien" der Filter vermeidest.



  • hustbaer schrieb:

    Das Interface muss ja nicht in der GUI definiert sein -- die GUI verwendet es bloss. Definiere es auf der Ebene wo auch das Filter-Interface definiert ist.

    Ich denke an sowas wie "Fenster aufmachen, um Parameter zu editieren". So eine Schnittstelle könnte ich ja schlecht in einer Dll implementieren, die keine GUI kennt. Wenn ich an der Stelle wieder dynamisch etwas aufrufe, könnte es gehen, wär aber auch wieder recht komisch. Könnte man wohl wieder deklarativ machen, vielleicht über Html, aber das Auswerten der Parameter wäre wieder kompliziert...



  • Eine ShowEditorDialog(HWND parentWindow) Funktion kann man so-gut-wie überall implementieren.
    Nur dass die DLL jetzt keine GUI Komponenten hat, heisst ja nicht dass sie keine bekommen kann 😃

    Sowas so weit zu abstrahieren dass es total von der GUI entkoppelt ist, ist halt richtig viel Arbeit. Vor allen wenn es auch nur halbwegs angenehm zu verwenden sein soll. Da muss man einen Haufen Property-Typen definieren, Constraints, Beschreibungen und was nicht noch alles. Und selbst wenn man sich den ganzen Aufwand antut, gibt es immer noch Dinge die man nicht oder nicht schön damit lösen/abbilden kann.

    HTML + JavaScript wäre natürlich eine andere Möglichkeit*. Wenn du nen fertigen HTML-Container hast, wo du über JavaScript aufrufbare eigene Funktionen reinhängen kannst (und diverse Sachen kontrollieren/verhindern, wie z.B. verhindern dass die HTML Seite externe Daten referenziert), dann ginge das vielleicht mit halbwegs vertretbarem Aufwand.

    Die Editor-Schnittstelle müsste dann lediglich string GetData(string name)/void SetData(string name, string value) Funktionen enthalten, die der Container dann über JavaScript zur Verfügung stellt. Mit denen könnte der JavaScript Code im Editor dann mit dem Objekt kommunizieren.

    *: Ich schreibe eine andere Möglichkeit, weil du damit erst wieder nix von der GUI entkoppelt hast. Die GUI "ist" dann einfach das HTML + JavaScript das vom Objekt kommt, also erst wieder Teil des Objekts und damit ist hier nix entkoppelt. Du verwendest bloss ein plattformunabhängiges System mit dem die GUI implementiert wird, und vermeidest dadurch eine Dependency auf irgend eine konkrete GUI Library/Technologie.


Log in to reply