Datencontainer programmieren



  • Soweit ich das sehe muss der Datencontainer wissen welcher Typ eingeliefert wird. Z.B. abonniert sich Modul4 DatenTypA. Das heißt wann immer ein DatenTypA eingeliefert wird muss ich Modul4 benachrichtigen. Also muss der DatenContainer wissen was eingeliefert wird, oder?

    Nebenbei: Kann ich als DatenContainer auch sehen WER ein DatenObjekt einliefert? Also wer die Methode einliefern(Daten 😉 aufruft?



  • fabske schrieb:

    Soweit ich das sehe muss der Datencontainer wissen welcher Typ eingeliefert wird. ...

    Ich sehe das anders. Ich würde die Datenhaltung (den Container) von den abgelegten Objekten trennen. Du hast doch schon "Daten" als eigenen Typen (Wrapper) ... mehr braucht der Container nicht zu kennen, um seine Arbeit zu erledigen.
    Die Konvertierung zwischen den konkreten Typen und "Daten" (der serialisierten Präsentation der Objekte) würde ich den Typen selbst und einer "factory" überlassen.

    Gruß,

    Simon2.



  • Hört sich interessant an, glaub aber nicht dass das geht. Was ist denn eine Fabrik genau, hast du ein passendes Beispiel. Das auf Wikipedia ist sehr mager. Die http://de.wikipedia.org/wiki/Fabrikmethode hab ich hingegen verstanden, aber die passt nicht, oder meinst du das so, dass ich eine Basisklasse Daten habe und alles weitere einfach ebenfalls ein Objekt ist, welches aus Daten verlinkt ist? Alle Daten sehen also gleich aus, nur ein Zeiger auf die konkreten unterschiedlichen Objekte ändert sich?

    Wie soll denn der Datencontainer einem konkreten Modul sagen, ein neues Datum von TypA ist für dich da, wenn er gar nicht weiß dass es sich um TypA handelt?



  • fabske schrieb:

    hustbaer schrieb:

    Ich würde vermutlich eine gemeinsame Basisklasse verwenden, und nur einen grossen Container der alles verwaltet. Wozu Code verfünffachen?

    Ok, und wie erkenne ich um was für einen konkreten Typ es sich handelt?

    Viele Wege führen nach Rom.

    Du könntest jeder Klasse eine eigene ID verpassen, und diese mit einer virtuellen Funktion in der Basisklasse abfragbar machen ("int id = obj->GetClassID()").

    Du könntest alle Möglichkeiten mit dynamic_cast durchprobieren.

    Du könntest auch nur die "Anlieferungs-Funktion" in der Container-Klasse fünffach machen, und dafür KEINE "Anlieferungs-Funktion" haben, die die Basisklasse annimmt. Damit wüsstest du den Typ dann implizit.

    Du könntest auch das Visitor-Pattern einsetzen, und die Aufgabe zu entscheiden wer benachrichtigt wird der Daten-Klasse übertragen. Dadurch könnte die Daten-Klasse dann - wenn sie möchte - auch zwei (oder mehr) Daten-Typen gleichzeitig "sein".

    Ich würde vermutlich einen der beiden letzten Wege wählen.

    Nun muss ich dieses konkrete Objekt irgendwie identifizieren, z.B. mit der Erstellungszeit als Index in der map.

    Was du als Key verwenden solltest/könntest, hängt wohl davon ab, nach was du "suchen" willst. Also was du als "ID" für so ein Daten-Dings hast, wenn du es anfinden willst.
    Wenn du dir die ID aussuchen kannst, dann würde ich *nicht* die Erstellungszeit verwenden. Entweder eine eigene ID, die du einfach mit jedem Daten-Stück hochzählst (sollte dann idealerweise 64 Bit sein). Oder irgendwas anderes mit dem du ein Daten-Stück wirklich *eindeutig* identifizieren kannst. z.B. einfach die Adresse des Objekts (**).

    *: dummerweise erlaubt C++ streng genommen nicht, dass man Adressen von beliebigen Objekten auf grösser/kleiner vergleicht. Man darf auf gleich/unglich testen, aber eben nicht grösser/kleiner. Real funktioniert es zwar, aber ich will dir nichts empfehlen, was laut Standard "nicht OK" ist.
    Es sollte aber gehen, wenn du eine Hash-Map Klasse zur Hand hast, die eine Hash-Funktion für Zeiger (void
    ) bereitstellt. Dann "garantiert" dir die Klasse dass die Implementierung der Hash-Funktion OK ist, und du musst dir darüber keinen Kopf machen.



  • Vielen Dank für die ausführliche Antwort hustbaer! Da sehr viele solcher Daten pro Minute anfallen werden halte ich persönlich den dynamic_cast für zu unperformant. Die vielen casts und if Abfragen sehen mir so aus, als ob das Resourcen frisst!

    Die "Anlieferungs-Funktion" für jeden der 5 "Basistypen" zu überladen kam mir noch gar nicht in den Sinn! Aber vielleicht hab ich mich schlecht ausgedrückt. Die 5 Typen sind Basistypen von denen jeder Modulersteller neue ableiten darf. Es gibt also in Wirklichkeit unbestimmt viele Typen, die zu einem der 5 Typen gehört, die aber alle wieder einen Vater haben (können, wäre ja auch ohne möglich).

    Neben dem Fabrikpattern, das ich immer noch nicht verstanden hab, wirst du nun das Visitorpattern in den Raum. Ich hab es mir angeschaut und folgende Idee verstanden: Jedes einzelne DatenObjekt, egal welchen Typs, soll seine Abonnenten selber verwalten? Damit spare ich mir eine große Tabelle in der alle Abonnenten aufgelistet sind. Ja?



  • fabske schrieb:

    ...
    Die "Anlieferungs-Funktion" für jeden der 5 "Basistypen" zu überladen kam mir noch gar nicht in den Sinn! Aber vielleicht hab ich mich schlecht ausgedrückt. Die 5 Typen sind Basistypen von denen jeder Modulersteller neue ableiten darf. Es gibt also in Wirklichkeit unbestimmt viele Typen, die zu einem der 5 Typen gehört, die aber alle wieder einen Vater haben (können, wäre ja auch ohne möglich)....

    Das war's, was ich meinte (war gestern nur zu müde für eine Ideenskizze).

    Wenn Du auf templates gehst, brauchst Du für die Erzeugung auch gar keine "5 Basistypen" - sie stören aber auch nicht: Du kannst halt alles erzeugen, für das entsprechende (template-)Spezialisierungen bestehen.

    Ich schau mal, ob ich eine lauffähige Ideenskizze hinbekomme...

    Gruß,

    Simon2.



  • Such vielleicht mal im Netz nach Publish/Subscribe. Da gibt es 'ne Menge Infos zu, uns so wie ich das sehe, könnte es genau das sein, was Du benötigst.



  • Damit spare ich mir eine große Tabelle in der alle Abonnenten aufgelistet sind. Ja?

    Nö, das Datenobjekt verwaltet die Abonnenten nicht selbst. Es wählt nur aus, wer benachrichtigt werden soll.

    class Data;
    class DataTypeA;
    class DataTypeB;
    class DataTypeC;
    class DataTypeD;
    class DataTypeE;
    
    class SubscriberList
    {
    public:
        void NotifyTypeASubscribers(DataTypeA* data) const;
        void NotifyTypeBSubscribers(DataTypeB* data) const;
        void NotifyTypeCSubscribers(DataTypeC* data) const;
        void NotifyTypeDSubscribers(DataTypeD* data) const;
        void NotifyTypeESubscribers(DataTypeE* data) const;
    
    private:
        // ...
    };
    
    class Data
    {
    public:
        virtual void NotifySubscribers(SubscriberList const& slist) = 0;
    };
    
    class DataTypeA : public Data
    {
    public:
        virtual void NotifySubscribers(SubscriberList const& slist)
        {
            slist.NotifyTypeASubscribers(this);
        }
    
    // ...
    };
    
    class DataTypeB : public Data
    {
    public:
        virtual void NotifySubscribers(SubscriberList const& slist)
        {
            slist.NotifyTypeBSubscribers(this);
        }
    
    // ...
    };
    
    // ...
    
    // nur als Beispiel was damit auch ginge:
    class DataTypeAB : public DataTypeA, public DataTypeB
    {
    public:
        virtual void NotifySubscribers(SubscriberList const& slist)
        {
            slist.NotifyTypeASubscribers(static_cast<DataTypeA*>(this));
            slist.NotifyTypeBSubscribers(static_cast<DataTypeB*>(this));
        }
    
    // ...
    };
    
    class Container
    {
    public:
        void FeedData(Data* data)
        {
            data->NotifySubscribers(m_slist);
        }
    
        // ...
    
    private:
        SubscriberList m_slist;
    
        // ...
    };
    


  • hustbaer schrieb:

    Ich würde vermutlich eine gemeinsame Basisklasse verwenden, und nur einen grossen Container der alles verwaltet. Wozu Code verfünffachen?

    Zustimmung.

    Ich hab zufällig gerade letzte Woche ein sehr ähnliches Modul mit Abonnementfunktion entwickelt.

    Eine Basisklasse -> mehrere abgeleitete Klassen -> ein Container

    Dazu kommen dann jeweils noch Informationen zu Menge und Art der Abonnenten, die auch mit in der Basisklasse enthalten sind. D.h. sobald die Menge der Abonnenten bei 0 ankommt, wird der entsprechende Datenstream abgemeldet und die Datenmenge wird gelöscht. Eigentlich genau so wie du das brauchst.

    Lässt sich mit der Basisklasse und ihren Ableitungen übrigens hervorragend und angenehm programmieren. Ist unter Ausnutzung der STL auch relativ wenig Code.



  • Hallo Leute! Ich melde mich erst jetzt wieder weil sich die Dimensionen stark erweitert haben. Vielleicht erkläre ich kurz was ich damit eigentlich vor hab:
    Die einzelnen DatenTypen sind Sensorwerte der Sensoren unseres autonom fahrenden Autos[/url]. Das bedeutet es werden pro Sekunden viele solcher Sensorwerte erstellt und nur für wenige Millisekunden überhaupt benötigt, danach wieder vernichtet. Das ganze soll ein Framework werden, wo sich jetzt noch nicht bekannte Module anmelden können und Sensorwerte abonnieren. Die Module können daraus selbst virtuelle Sensorwerte berechnen (neue DatenTypen) und wiederum dem Datenmodul übergeben um wiederum Abonnenten auszuliefern. Ein riesiger Kreislauf eben.

    Ich habe mich die letzten Tage in die Sache eingearbeitet und mit zum Beispiel das [url="http://de.wikipedia.org/wiki/Observer_(Entwurfsmuster)"Observer Paddern"[/url] klar gemacht (danke Tachyon). Vielen Dank für deinen Kode hustbaer, das ist eine sehr gute Idee. Und wenn mit It0101 seinen Kode mal zeigen könnte wäre das sicher sehr nützlich.

    Nur wurde ich darauf aufmerksam gemacht, dass ich ein solches Framework ohne Threads vergessen kann. Ich hatte bisher gar nichts mit Threads zu tun und mich auch darin mal eingelesen.

    So wie ich das identifizieren konnte, brauche ich ein Modell um die einzelnen Module als Threads zu realisieren, und dabei ein Event-Management zur Synchronisation. Außerdem muss ich wohl die einzelnen DatenTypen-Objekte recyceln, denn die Speicherverwaltung würde zu viel Resourcen fressen wenn ich ständig so viele Objekte anlege und wieder vernichte.

    Das ganz bereitet mir grad Kopfzerbrechen. In der Wikipedia steht zum Observer bei Nachteile: "Bei der gerade durchgeführten Observierung eines Objektzustands kann es notwendig sein, einen konsistenten Subjektzustand zu garantieren. Dies kann durch synchrone Aufrufe der Notifizierungsmethode des Observers sichergestellt werden. In einem Multithreading System sind evtl. Lockingmechanismen oder Threads mit queuing zur Observer-Notifizierung erforderlich."


Anmelden zum Antworten