function pointer auf member-funktionen
-
Hallo,
habe eine oft diskutierte Frage.
Ich habe eine Klasse, die eine Menge (z.Z. 3) Funktionen besitzt, auf die ich einen Funktionszeiger richten möchte. Ich habe bereits im Forum gelesen, dass man statische Memberfunktionen nehmen kann, auf diese kann man dann die Funktionszeiger richten. Aber wenn man eine Menge Funktionen hat, dann muss man auch eine Menge statische Funktionen dafür schreiben.
Geht das nicht auch einfacher bzw. sinnvoller?
MfG,
Johannes
-
Nächster Halt Google: c++ method pointer.
In Fahrtrichtung links anklicken.
-
http://www.oleeichhorn.com/articles/040617-C++_method_pointer.html
Das Problem ist, dass das bei mir nicht funktioniert. Ich arbeite mit pthreads:
typedef void* (MyServer::*fptr)(void*); //... MyServer server1(port); // set up the server pthread_create(&run_server_thread, NULL, (fptr)&MyServer::run, NULL);Es gibt einen compiler-Error:
./myserver.cpp:77: error: cannot convert ‘void* (MyServer::)(void)’ to ‘void* ()(void)’ for argument ‘3’ to ‘int pthread_create(pthread_t*, const pthread_attr_t*, void* ()(void), void*)’Der Cast schien mir notwendig, da
void MyServer::run(void)nicht zu dem Funktionspointer eines pthreads passt:
void *(*start_routine)(void*)Und ich weiß nicht, wie pthread_create() den func pointer aufruft... pthread_create() weiß evtl. nicht einmal, welches Objekt ich meine!
-
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.