function pointer auf member-funktionen





  • Das mit den callbacks sieht interessant aus, danke.

    Danke auch hustbaer für die Trampolin-Funktion. Wozu braucht man den static_cast? Ginge es hier nicht auch ohne?



  • voidpointer schrieb:

    ...Wozu braucht man den static_cast? Ginge es hier nicht auch ohne?

    Nunja ... einen void-Pointer kann man nunmal nicht dereferenzieren (und schon gar nicht mit zu einer "Run()"-Methode.

    Gruß,

    Simon2.



  • ach bingo! ok thnx jetzt verstehe ich es 🙂

    sprich: hier würde man diese template-magie anwenden können? (hier ist MyServer eine abgeleitet Klasse von Server, in diesem Fall also keine Templates notwendig)

    und das Trampolin lässt noch eine Frage offen: ich brauche eines für jede Funktion in der Klasse!



  • Naja mach es einfach so:

    class foo
    {
    public:
        virtual void bar();
    
    private:
        static void _bar(void* ptr_inst) { static_cast<foo*>(ptr_inst)->bar(); }
    };
    
    foo inst;
    pthread_create(&run_server_thread, NULL, &foo::_bar, &inst);
    

    dann hast de es alles in der Klasse 😉 (Bei WinAPI ist das wohl die einfachste Möglichkeit, alles in eine Klasse packen zu können!)



  • und das Trampolin lässt noch eine Frage offen: ich brauche eines für jede Funktion in der Klasse!

    Naja, DA z.B. lässt sich die Template Magic anwenden.
    Ganz einfach könnte es z.B. so gehen:

    template <class T> void trampoline_function(void* p)
    {
        std::auto_ptr<T> t(static_cast<T*>(p));
        (*t)();
    }
    
    template <class T> struct member_function_wrapper
    {
        member_function_wrapper(T* t, void (T::*f)()) :
            m_t(t), m_f(f)
        {
        }
    
        void operator()()
        {
           (m_t->*m_f)();
        }
    
        T* m_t;
        void (T::*m_f)();
    };
    
    template <class T> int my_thread_create(pthread_t* thread, const pthread_attr_t* attr, T& t, void (T::*f)())
    {
    	std::auto_ptr<member_function_wrapper<ttest> > mfw(new member_function_wrapper<ttest>(t, f));
    	int rc = pthread_create(thread, attr, &trampoline_function<member_function_wrapper<ttest> >, mfw.get());
    	if (rc)
    		return rc; // fehler
    
    	mfw.release(); // gehört jetzt dem thread
    	return 0;
    }
    


  • 👍 😋 Cool !

    Gruß,

    Simon2.



  • Vielen Dank für diesen Code! Leider habe ich Schwierigkeiten ihn zu verstehen... der Code muss unbedingt (auch für andere) gut verständlich sein, es soll eine library werden.



  • Hi,

    also ich hab' ihn ja nicht verfasst, aber da "selbst erklären" der wichtigste Schritt beim Lernen ist, versuch ich's mal:

    1.) Statt pthread_create(..., void*) wird also my_thread_create(..., pointer_auf_mein_Objekt, member_function_pointer_auf_gewünschte_Memberfunktion) aufgerufen.
    Dabei ist der Typ des übergebenen Objekts parametrisierbar (= kann jeder mögliche Typ und trotzdem typsicher sein). Es wird davon ausgegangen, dass die Memberfunktion keinen Rückgabetyp hat und keinen Parameter entgegennimmt - das ist zwar eine Einschränkung, aber nur eine, die man bereits von pthread_create() kennt.

    2.) Diese erzeugt ein Objekt vom Typ member_function_wrapper mit folgender Aufgabe:
    - die Zeiger auf das Objekt und auf die Memberfunktion bündeln
    - die Memberfunktion per operator() aufrufbar zu machen
    Vorteil: Man braucht jetzt nur noch EINEN Zeiger (auf member_function_wrapper) und nicht mehr ZWEI (Objekt und Memberfunktion).

    3.) Die Trampolin-Funktion hast Du ja schon kennengelernt: Sie castet einen void-Pointer auf einen Objekt-Pointer und ruft eine Memberfunktion davon auf. Damit sie nicht mehr zu wissen braucht, wie die Funktion heißt, bietet der member_function_wrapper sie hinter dem operator() an.

    4.) Als nettes Schmankerl wird das member_function_wrapper-Objekt dynamisch erzeugt (damit es nach Ablauf von my_thread_create() nicht vernichtet wird) und "schenkt" es dem neu erzeugten Thread (mittels auto_ptr::release()).

    Also Aufruf-Struktur:

    • my_thread_create() =>
    • pthread_create() =>
    • trampoline_function() =>
    • member_function_wrapper::operator() =>
    • Objekt::MemberFunktion

    Ach so, aufgerufen wird das so:

    struct myClass {
       void machWas();
    };
    
    int main() {
       std::auto_ptr<myClass> myC(new myClass);
       // pthread-Zeug überspringe ich jetzt mal
       return my_thread_create(..., *(myC.get()), myClass::machWas);
    }
    

    Dabei fällt mir wohl ein Tippfehler bei hustbaers Code auf: in my_thread_create() sollte "ttest" wohl "T" lauten:

    hustbaer schrieb:

    ...
    template <class T> int my_thread_create(pthread_t* thread, const pthread_attr_t* attr, T& t, void (T::*f)())
    {
    	std::auto_ptr<member_function_wrapper<ttest> > mfw(new member_function_wrapper<ttest>(t, f));
    ... &trampoline_function<member_function_wrapper<ttest> >,...
    

    =>

    ...
    template <class T> int my_thread_create(pthread_t* thread, const pthread_attr_t* attr, T& t, void (T::*f)())
    {
    	std::auto_ptr<member_function_wrapper<T> > mfw(new member_function_wrapper<T>(t, f));
    ... &trampoline_function<member_function_wrapper<T> >,...
    

    Gruß,

    Simon2.



  • Äh.
    Ich bin auch was doof 🙂
    Die trampoline_function muss natürlich garkein Template sein. Dazu muss man nur den member_function_wrapper von einer abstrakten Basisklasse ableiten und einen virtual call verwenden statt non-virtual...

    Naja, egal, falsch isses in der Template Version ja nicht.

    Ansonsten: man verwende einfach Boost.Thread für sowas. Wenn man sich den Threading Teil aus irgendeinem Grund doch selbst stricken will kann man immernoch boost::function verwenden - damit kann man dann fast alles als Thread--Funktion verwenden.


Anmelden zum Antworten