Herausfinden, wer die Callbackfunktion ausgeführt hat.



  • Hallo Zusammen,

    das hier ist mein erster Post hier im Forum, weil ich eine Lösung für eine Problemstellung suche, die ich nicht finde.

    Der Titel ist unglücklich gewählt, aber mir fiel nichts besseres ein. Mir sind die gängigen Vokabulare nicht exakt bekannt, dh es kann sein, dass ich die einen oder anderen C++ Konventionen falsch nutze oder bezeichne.
    So zum Thema:

    Grob geht es um ein inverses Observer Pattern. Eine Klasse Y wird von mehreren Klassen benachrichtigt, wenn ein Fehlerfall vorliegt. Die Nachricht ist sehr simple. Die Klasse Y bekommt ein true, wenn in einer der Observer Klassen ein Fehler aufgetreten ist, ansonsten sollen sie ein false liefern.
    Das Problem ist jetzt, ich will wissen, welche Klasse das true oder false gesetzt hat. Ich will dann in einer Variable in Abhängigkeit der Observer Klassen ein Bit setzen, denn sonst würde ich ggf. das true des anderen Observers löschen.

    Klasse Y:
    
    virtual void ErrorOccured(bool isError)
    {
     if(isError)
    { 
     memberVariable |= setze Bit in Abhängigkeit einer Observerklasse, die diese Funktion aufgerufen hat.
    }
    else
    {
    memberVariable &= ~losche Bit in Abhängigkeit....
    }
    }
    

    Habt ihr eine Idee?
    Viele Grüße.



  • Wieso muss Die Klasse genau wissen vom welchen Observer aufgerufen wurde?
    Und wiso als Bit? Dadurch ist die parallele Verwendbarkeit einer Instanz dieser Klasse doch sehr eingeschränkt (je nachdem wie groß diese "membervariable" ist).

    Eine möglichkeit wäre es die ErrorOccured Methode um einen zusätzlichen Parameter zu erweitern.
    Dieser Parameter würde dann die Information liefern um welchen Observer es sich handelt.



  • @TK-KT sagte in Herausfinden, wer die Callbackfunktion ausgeführt hat.:

    Die Klasse Y bekommt ein true, wenn in einer der Observer Klassen ein Fehler aufgetreten ist,

    Würden Exceptions hier dein Problem lösen?



  • Ist es nicht möglich das API entsprechend abzuändern?



  • @Quiche-Lorraine

    Wir nutzen keine Exceptions. Das ist eine embedded Anwendung. Der Fehler wird dann in Form einer Ansteuerung ausgegeben.

    Die Möglichkeit, die API zu ändern ist vorhanden. Ich kann natürlich mitgeben, wer den Callback aufgerufen hat, aber dann muss der User extrem aufpassen, dass er den Parameterwert nicht doppelt belegt.



  • Die Alternative wäre es über die Compiler API zu versuchen herauszubekommen woher der Aufruf kam. Aber da sieht man üblicherweise nur den Callstack und nicht das Objekt.



  • na ja, mal einfach so in die Runde geworfene Dinge

    1. Woher kommt diese Funktion? Aus einem Objekt, dessen Schnittstellen-Anforderung du sehen kannst? Wenn ja, könntest du evtl. ein Zwischen-Objekt bauen. Ruf die Funktion auf das auf und dieses Zwischenobjekt kennt dann seine Id. Dann bliebe alles unter deine Kontrolle.

    2. Du könntest den Aufruf über ein Konstrukt machen, das du mit #define und einem Präprozessor-Counter machst? Oder den Codedateinamen und die Codezeile übergeben um intern irgendwie daraus was abzuleiten.



  • "...Eine Klasse Y wird von mehreren Klassen benachrichtigt…" von den Observer Klassen?

    Warum übergibst Du der Methode ErrorOccured nicht einfach eine Referenz auf die jeweilige Observer Klasse. Dann hat ErrorOccured gleich das betreffende Objekt?



  • @Helmut-Jakoby

    Sehr guter Hinweis.
    Danke, muss ich mal ausprobieren!



  • Der Vorschlag war eigentlich schon in der ersten Antwort von @firefly enthalten 😉



  • @hustbaer Hast recht, aber ich muss den Vorschlag mit der Übergabe einer Referenz auf ein Objekt revidieren.
    Denn, ich will in Abhängigkeit eines Objekts ein Bit setzen. Ich versuche es mal zu erklären:

    memberVariable ist 8 Bit groß. Jedes Bit wird einem Observer zugeordnet.
    Observer 1 setzt memberVariable bei true den Bit 1 auf 1 und bei false löscht er es wieder.
    Observer 2 setzt Bit 2
    usw
    bis Observer 8.

    Warum das ganze?
    Weil angenommen, Observer 1 bis 8 werden nacheinander ausgeführt, dann würde, wenn Observer 1 einen Fehler meldet, dann bestünde die Gefahr, dass die anderen Observer die memberVariable von true auf false setzen. Daher verodere ich die memberVariable.
    Mit einer Referenz auf ein Objekt kann ich kein Bit setzen.
    Ich kann höchstens nebem dem boolschen Wert eine Art Objektnummer 1 bis 8 mit übertragen. Das Problem ist, ich muss bei der Übergabe der Objektnummern höllisch aufpassen, die Objektnummern nicht doppelt zu belegen.

    Ich dachte, da ich ein blutiger C++ Anfänger bin, dass es eine elegantere Methode gibt.....



  • Ist sichergestellt, dass es maximal 8 Observer gibt?
    Weil wenn ja könnte man folgendes machen.
    Alle Observer leiten von einer Basis klasse ab.
    Die Basisklasse enthält einen member welcher die "Objektnummer" darstellt. Und stellt eine get-Methode bereit.
    Im Construktor der Basisklasse wird mit hilfe einer statischen variable die "Objektnummer" generiert.

    Die "Error occured " methode nimmt dann eine reference von dieser Basisklasse als zusätzlichen Parameter

    Das ganze funktioniert nur wenn garantiert ist, dass das erstellen der Observer Instanzen nicht parallel erfolgt (z.b. via threads). Bzw. man müsste dann die Generierung der "Objektnummer" serialisieren (z.b. via mutex)

    Diese Basisklasse könnte im groben so aussehen.
    Wichtiger Hinweis! Folgenden code sollte man so nicht direkt übernehmen sondern eher nur als ein beispiel verstehen, da ich hier nicht alle möglichen fallstricke bedacht habe.

    class objektnumber {
        private:
            int id;
        public:
            int getId()const{ return id;}
        protected:
            objectnumer() {
                static int nextId = 0;
                id = nextId;
                ++nextId;
            }
    }
    

    Das die erste id/objektnummer 0 ist, kann man dann folgendes recht einfach machen um das passende bit zu setzen/zu löschen:

    void ErrorOccured(bool isError, const objektnumber& object)
    {
        if (isError) {
            memberVariable |= 1<<object.getId();
        } else {
            memberVariable &= ~(1<<object.getId());
        }
        
    }
    


  • @firefly Ich danke dir für die Idee.
    Das werde ich austesten.

    Die Id Nummer müssten aber in 2er Potenzen verteilt werden.
    Dh Objekt 1 ist 1, Objekt 2 ist 2, Objekt 3 ist 4.
    Id Nummer = 2^(Anzahl der Objekte-1)



  • Du mußt nur beim ver'oder'n die 2er-Potenz erstellen:

     memberVariable |= (1 << n); // n: 0 ... 7
    

    Wenn du doch lieber Referenzen statt der Zahlenwerte übergeben willst, dann erzeuge dir eine std::map<const Class*, int> (welche du einmalig füllst und dann in deiner Funktion aufrufst).



  • @TK-KT sagte in Herausfinden, wer die Callbackfunktion ausgeführt hat.:

    @firefly Ich danke dir für die Idee.
    Das werde ich austesten.

    Die Id Nummer müssten aber in 2er Potenzen verteilt werden.
    Dh Objekt 1 ist 1, Objekt 2 ist 2, Objekt 3 ist 4.
    Id Nummer = 2^(Anzahl der Objekte-1)

    Nein darf sie nicht. Die nummer stellt dar um welches bit es sich handelt.
    Durch die "bitwise shift operation" (<<) in der ErrorOccured wird dann ein wert generiert in dem nur das entsprechende Bit gesetzt wird.
    2 Beispiele:
    Wenn ID 7 ist, dann werden alle gesetzten Bits um 7 stellen nach links verschoben.
    In diesem Falle ist das Ergebnis dann binär 1000 0000 oder dezimal 128.
    Wenn die ID 0 ist, dann bleibt es bei dem wert von 1 welches binär 0000 0001 (Bei 8Bit) entspricht.

    Meine Erklärung wie die "bitwise shift operation" funktioniert ist sehr vereinfacht. Sollte aber ausreichen um das Prinzip zu verstehen.



  • @Th69 sagte in Herausfinden, wer die Callbackfunktion ausgeführt hat.:

    Du mußt nur beim ver'oder'n die 2er-Potenz erstellen:

     memberVariable |= (1 << n); // n: 0 ... 7
    

    Siehe meine ErrorOccured beispiel Implementierung wo genau das passiert 😉



  • @firefly: Sorry, nicht gesehen (manchmal werden Beiträge nicht automatisch angezeigt, wenn die Seite geöffnet ist und ich muß nochmal explizit F5 drücken).


Anmelden zum Antworten