function pointer auf member-funktionen
-
Grundsätzlich gibts 2 Möglichkeiten:
- Wenn der Funktionszeiger ein void* Parameter hat kannst du ne Trampolin-Funktion verwenden (s.u.)
- Wenn der Funktionszeiger kein geeignetes Parameter hat kannst du nurnoch Thunks verwenden (=ganz furchbar garstig und 0% portierbar).
ad 1.:
void TrampolinFunktion(void* p) { static_cast<MyServer*>(p)->Run(); } void foo() { // ... MyServer server1; pthread_create(&run_server_thread, NULL, TrampolinFunktion, &server1); // ... }Mit ein wenig Template-Magic lässt sich das auch generisch für beliebige Klassen und Memberfunktionen implementieren.
-
Zum nachlesen:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-204341-and-highlight-is-funktionszeiger.html
-
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.