pointer auf member function



  • roflo schrieb:

    Ich verwende immer noch den gute alte davor-schreiben-stil. Ansonsten sieht das schon ziemlich hacky aus, std::function passt hier wohl besser.

    ja ich wollte ursprünglich auch std::function verwenden. was mich da aber davon abgebracht hatte, war das std::function recht groß ist.
    sizeof(std::function<void(void*)>) zeigt mir 40 bytes an, und mein signal braucht grad mal 8 bytes.
    nachdem ich aber nur member function pointers brauche, bin ich auch nicht bereit dafuer das 5-fache zu zahlen, vorallem dann nicht,
    wenn mein signal standardkonform ist.
    wenn ich als 64 bit kompiliere ist der unterschied nicht mehr ganz so groß: 64 bytes zu 16 bytes.
    ansonsten muss ich sie fallen lassen.
    gibts von std::function auch sowas wie eine light version ?

    Meep Meep



  • Solange du nicht Tausende von signalen hast und nicht den üblichen Desktop-Rechner abdecken willst, ist std::function die beste Wahl 🙂
    Kann auch sein, dass es in der release-version kleiner ist.



  • mit VC 2015 sind die größen in debug und release die selben.
    jedes object hat 63 signale, aber ich hab 16384 objekte.
    und da macht es schon sehr viel aus



  • Wo ist die Implementation dieser Funktion?

    template<class FUNC>
    auto set(void *object, FUNC func) -> void;
    

    Die wäre mal interessant. Vor allem weil es einen void Pointer für das Objekt nimmt und die Funktion als Template. Wie speicherst du das dann in deinem Member Function Pointer der einer festen Signatur folgt?

    Meep Meep schrieb:

    mit VC 2015 sind die größen in debug und release die selben.
    jedes object hat 63 signale, aber ich hab 16384 objekte.
    und da macht es schon sehr viel aus

    Es geht. Macht also ~1Mio Signale und damit 40MB für std::function und 8MB für dein Signal.



  • hi sebi, hier die signal::set

    template<class FUNC>
    inline auto signal::set(void * object, FUNC func) -> void
    {
       m_object = reinterpret_cast<generic_class*>(object);
       m_function = reinterpret_cast<function>(func);
    }
    


  • Das ist mit großer Sicherheit nicht Standardkonform. Pointer to Member Functions sind komische Tiere. Ich finde zwar gerade nichts im Standard aber dein Code funktioniert wahrscheinlich nicht wenn virtual im Spiel ist oder wenn die Größe der Parameter/Rückgabewert nicht stimmt.



  • virtuelle methoden funktionieren genau so. da gibts keine probleme.

    das die größen der parameter/rückgabewert stimmen muss, ist ja logisch.



  • Mal ein Beispiel bei dem es kaputt geht:

    class A
    {
      int x = 5;
    public:
      void foo(void* sender)
      {
        cout << "foo " << x << endl;
      }
    };
    
    class B : public A
    {
    public:
      virtual void bar()
      {
      }
    };
    
    int main()
    {
      B b;
      signal x;
      x.set(&b, &B::foo);
      x.call(NULL);
    }
    

    Das gibt bei mir foo 17599292 (oder irgendeine andere Zahl aus). Zugegeben ist das jetzt nicht wegen deiner Member Function Pointer sondern wegen dem reinterpret_cast auf das Objekt. Dadurch das die Klasse B aufeinmal virtuelle Funktionen hat gibt es einen Offset zwischen einem Pointer auf A und Pointer auf B des gleichen Objekts. Ließe sich auch mir Mehrfachvererbung erreichen. Vermutlich kann man auch noch andere Konstruktionen finden die nicht funktionieren.



  • Ach.. Sieht ja (fast) genauso aus wie die Klasse, die ich mir letztens geschrieben habe (bis auf die Lambda-Syntax (erst auto und dann noch den Rückgabetyp - das ist doch doppelt gemoppelt), und noexcept fehlt bei mir auch (ist das für den Compiler wirklich wichtig, oder nur für den Menschen?)).

    Ist ja auch kein Wunder. Denn, 1., wie will man das sonst machen*, wenn man 2., nicht wirklich verstanden hat, was hinter boost::function und boost::bind (od std::..) steckt (so wie ich). In wxWidgets gibt es auch eine Bind<>()-Funktion, mit der man an bel. Funktionen und Methoden binden kann. Wird wohl so ähnlich laufen, aber wie genau - da bin ich überfragt.
    Vielleicht kann mir das jemand erklären, ohne jetzt den Thread hier einnehmen zu wollen..

    *Allerdings habe ich irgendwo gelesen, dass man Mathodenzeiger eigentlich nur zum Zwischenspeichern umcasten, und zur Benutzung wieder rückcasten darf - sehr flexibel...

    Erstmal zurück zu der bereits implementierten Klasse..
    Ich war/bin mir nicht sicher wie regelkonform das ist. Nach meiner Vorstellung ist eine Methode einfach eine Funktion irgendwo im Speicher, die als zus. verstecktes Arg. den this-Ptr ihrer Klasse übergeben bekommt - egal zu welcher Klasse die Methode jetzt konkret gehört. Daher verstehe ich nicht, warum man beim Methodenzeigern die Klasse mit berücksichtigen muss.
    Anders gesagt, sehe ich nicht, warum der Klassenname zur Funktionssignatur gehören sollte...
    Eigentlich gibt es doch nur Funktionszeiger (die ohne verstecktes "this"), und Methodenzeiger (die mit "this"), aber nicht Methodenzeiger von Klasse A, Methodenzeiger v. Klasse B, etc..
    Also nach dieser Vorstellung wäre es kein Problem, A::MethodenPtr in B::MethodenPtr zu casten (restliche Signatur stimme überein).
    Aber so 100%ig sicher bin ich mir nicht, da ich ja nicht weiß, ob meine Annahme immer (für alle Compiler/Plattformen/..) gilt.

    An virtuelle Methoden habe ich noch garnicht gedacht. Da habe ich auch überhaupt keine Vorstellung davon, wie ein Methodenzeiger damit umgeht.
    Könnte ja sein, dass das der Grund dafür ist, warum zw. A::MethPtr und B::MethPtr unterschieden wird, würde also die obige Annahme stören.
    Kann mir jemand erklären, warum gecastete Methodenzeiger mit virtuellen Methoden klarkommen, wenn sie es denn tun..?!

    Vielleicht zum Schluss noch kurz das Grundproblem: Es geht hier doch nur darum, aus einer Senderklasse heraus eine, bei der Implementierung nicht weiter bekannte, Empfängerklasse anzusprechen (stimmts?). D.h. meine Signal-Klasse kann keine Infos über den Empfänger besitzen. Alle Infos müssen erst bei der Verbindung von Sender und Empfänger durch eine gemeinsame Oberklasse bekannt gemacht werden. So weit so richtig? - Falls ja, nochmal die Frage: Wie will man das sonst machen?!
    Die Alternative, also ohne Signal-Klasse, wäre eine Sendefunktion für jede mögliche Empfängerklasse zu implementieren, oder sich eben auf genau eine Empfängerklasse zu beschränken - also keine wirkliche Alternative.
    Und noch eine: Warum gibt es dafür kein schönes Sprachmittel?!



  • Ich hatte vorhin zu Member Function Pointer auch noch diesen Artikel gefunden: https://blogs.msdn.microsoft.com/oldnewthing/20040209-00/?p=40713 Ich habe ihn auch nur überflogen aber eine schonmal interessante Tatsache war, dass Memberfunction Pointer unterschiedliche Größe (abhängig von der Klasse) haben können.

    Die Technik die std::function benutzt nennt sich übrigens Type Erasure. Find gerade keinen guten Übersichtsartikel aber die Grundidee ist eine abstrakte Basisklasse mit der gewünschten Funktionalität (also hier eine Funktion mit einer bestimmten Signatur) zu haben und dann eine Template Klasse die von dieser erbt. Durch den Template Parameter in der abgeleiteten Klasse kann man beliebiges Zeug speichern ohne das der Typ nach außen sichtbar ist weil man sich das Ganze nur als Pointer auf die Basisklasse speichert.



  • Wie wärs einfach mit einer abstrakten Basisklasse, von der der Signalhandler erbt und eine 'OnSignal' methode überschreiben kann?



  • @sebi
    ich waere ehrlich gesagt nie auf die idee gekommen die klasse so zu verwenden.
    ich verwende sie eigendlich nur um schreibarbeit zu sparen, wie sie mir der abstarkten basisklasse aus roflo´s vorschlag entstehen wuerde


Anmelden zum Antworten