Pointer innerhalb Klasse auf Memberfunktion
-
Hallo,
ich habe vorweg sehr viel gegoogelt, aber keinen Lösungsansatz gefunden.
Es geht um folgendes:Ich habe eine Klasse innerhalb der ich zwei Threads starten möchte, die eine der Klassenmethoden aufrufen.
Das ganze sieht wie folgt aus:
this->_acceptThread = new Threading::HThread( &this->_accept, NULL, Threading::PREPARED, true ); this->_receivingThread = new Threading::HThread( &this->_receiveData), NULL, Threading::PREPARED, true );Es geht genau um den ersten Parameter. Es wird folgendes erwartet:
typedef unsigned int (__stdcall *THREAD_ROUTINE)(void*);Wie bekomme ich einen Zeiger auf diese Methode? Der Hintergrund ist mir klar. Jede Instanz dieser Klasse bekommt ihre eigenen Methoden. Daher ist es mir ohne Instanz nicht möglich die Adresse der instanzierten Klassenmethode zu bekommen.
Dennoch muss doch die Möglichkeit bestehen, an die Adresse der Instanz zu kommen.
-
Mach die Thread-Startup-Methoden statisch. Soweit ich weiß, kannst du die Methodenzeiger nicht ohne Instanz bekommen, daher ist das Static-Deklarieren gängig.
Gruß Kimmi
-
Ok. Angenommen Threading::HThread ist ein richtig billiger Wrapper für eine Betriebssystemfunktion, die Threads starten kann. Weiter angenommen, der Konstruktor erwartet Parameter a la
1. Funktionszeiger void(__stdcall*)(void*)
2. Zeiger für den Kontext (void*), der an die Funktion übergeben werden soll
3. weitere Optionen, flags, ...Rate mal, wofür dieser "Kontext-Zeiger" da ist.
Probier mal so etwas wie das hier:
extern "C" { void __stdcall transmogrify(void* context) { static_cast<MeineKlasse*>(context)->elementfunktion(); } } ... MeineKlasse* p = ...; Threading::HTread t (&transmogrify, p, ...);Es ist aber schon schade, dass der Konstruktor nicht so schlau ist und ein "normales" Funktionsobjekt akzeptieren kann -- so, wie es boost::thread kann und demnächst auch std::thread können wird.
Gruß,
SP
-
kimmi schrieb:
Mach die Thread-Startup-Methoden statisch. Soweit ich weiß, kannst du die Methodenzeiger nicht ohne Instanz bekommen, daher ist das Static-Deklarieren gängig.
Währendem das Objekt vorhanden, also die Instanz gebildet, ist, muss ich doch irgendwie an dessen Adresse kommen, oder?
Ich habe es schon mit folgender Variante probiert:
void HClasses::ServerSocket::_processMessages(void* _instance) { this->_receivingThread = new Threading::HThread( (HClasses::ServerSocket*)(_instance)->_receiveData), NULL, Threading::PREPARED, true ); }Will aber auch nicht compilieren.
Sebastian Pizer schrieb:
Es ist aber schon schade, dass der Konstruktor nicht so schlau ist und ein "normales" Funktionsobjekt akzeptieren kann -- so, wie es boost::thread kann und demnächst auch std::thread können wird.
Welche Art von Parameter sollte er dann erwarten?
-
Threading::THREAD_ROUTINE p = static_cast<HClasses::ServerSocket*>(_instance)->_accept;Das bringt folgenden Fehler:
Dem Funktionsaufruf fehlt die Argumentliste. Verwenden Sie "&HClasses::ServerSocket::_accept", um einen Zeiger auf den Member zu erstellen.Funktioniert also auch nicht.
Baue ich es so auf:
Threading::THREAD_ROUTINE p = (Threading::THREAD_ROUTINE)(static_cast<HClasses::ServerSocket*>(_instance)->_accept);sagt er:
'Typumwandlung': 'overloaded-function' kann nicht in 'HClasses::Threading::THREAD_ROUTINE' konvertiert werden
-
FrEEzE2046 schrieb:
Ich habe es schon mit folgender Variante probiert:
void HClasses::ServerSocket::_processMessages(void* _instance) { this->_receivingThread = new Threading::HThread( (HClasses::ServerSocket*)(_instance)->_receiveData), NULL, Threading::PREPARED, true ); }Was ist das denn für ein Unsinn?
Wie ist denn die Signatur Deines HThread-Konstruktors?
Was willst Du eigentlich hier machen?
Warum schreibst Du hier immernoch "NULL"?FrEEzE2046 schrieb:
Sebastian Pizer schrieb:
Es ist aber schon schade, dass der Konstruktor nicht so schlau ist und ein "normales" Funktionsobjekt akzeptieren kann -- so, wie es boost::thread kann und demnächst auch std::thread können wird.
Welche Art von Parameter sollte er dann erwarten?
Siehe boost::thread.
-
Sebastian Pizer schrieb:
Was ist das denn für ein Unsinn?
Wie ist denn die Signatur Deines HThread-Konstruktors?
Was willst Du eigentlich hier machen?
Warum schreibst Du hier immernoch "NULL"?1. Ich danke dir für deine Hilfe, aber geht`s vielleicht auch freundlicher? Ich würde nicht fragen, wenn ich die Antwort kennen würde. Ich glaube dass vergessen hier einige.
2. Ich schreibe "NULL", weil im 2. Parameter die "Parameter für die Thread-Funktion" übergeben werden. Da diese jedoch keine hat, ist NULL ziemlich logisch für mich.
Es geht mir im Prinzip doch nur darum, wie ich einen Zeiger auf die Methode bekommen kann. Den Aufruf der Thread-Klasse bekomme ich schon hin.
3. Ich habe keine Ahnung welche Parameter boost::thread erwartet. Ja, ich habe danach gegoogelt, aber nur Tutorials gefunden, in denen boost::thread benutzt wird. Ich sehe daran auch, dass man "normale" Funktionen übergeben kann, stellt sich nur die Frage wie das gemacht wird. Wenn du es mir sagen kannst, bin ich dir dankbar.
-
FrEEzE2046 schrieb:
1. Ich danke dir für deine Hilfe, aber geht`s vielleicht auch freundlicher? Ich würde nicht fragen, wenn ich die Antwort kennen würde. Ich glaube dass vergessen hier einige.
Ja, ich bin patzig geworden. Du hast scheinbar meinen Beitrag ignoriert.
FrEEzE2046 schrieb:
2. Ich schreibe "NULL", weil im 2. Parameter die "Parameter für die Thread-Funktion" übergeben werden. Da diese jedoch keine hat, ist NULL ziemlich logisch für mich.
Und das wäre die Gelegenheit gewesen, der Funktion einen Zeiger auf das Objekt zu übergeben, auf dem Du eine Methode aufrufen willst (siehe meine erste Antwort).
FrEEzE2046 schrieb:
Es geht mir im Prinzip doch nur darum, wie ich einen Zeiger auf die Methode bekommen kann. Den Aufruf der Thread-Klasse bekomme ich schon hin.
Einen solchen Zeiger kannst Du aber nicht gebrauchen. Du gibst dem HThread-Konstruktor zwei Zeiger. Einen void (__stdcall*)(void*) und einen *void, der dann an die Funktion gegeben wird. Du erzeugst Dir einfach eine freie Wrapper-Funktion mit der Signatur und __stdcall-Aufruf-Konvention, castest den void* in ein MeineKlasse* um, und rufst die Elementfunktion auf. Als void*-Parameter übergibst Du dann einen Zeiger auf das Objekt.
FrEEzE2046 schrieb:
3. Ich habe keine Ahnung welche Parameter boost:thread erwartet.
Schon klar. Nachgucken musst Du aber selbst mal.
Gruß,
SP
-
Tut mir wirklich leid, aber ich verstehe zwar was du meinst, weiß aber nicht wie genau ich das umsetzen soll.
Was genau soll ich, deiner Meinung nach, denn als void (__stdcall)(void*) - also ersten Parameter - übergeben?
&((HClasses::ServerSocket*)_instance)->_acceptWenn ich es caste bekomme ich immer noch die Fehlermeldung:
Ungültige Operation auf Ausdruck einer gebundenen Memberfunktion_instance ist ein void*
-
Was genau verstehst Du denn nicht an dem 3. Beitrag dieses Threads?
-
Naja, hab ich doch geschrieben. Ich weiß nicht wie ich die Thread-Funktion übergeben soll.
-
Ich habe doch ein Beispiel dabei gehabt, was die Übergabe zeigt.
-
Sebastian Pizer schrieb:
Ich habe doch ein Beispiel dabei gehabt, was die Übergabe zeigt.
Wenn du das hier meinst:
extern "C" { void __stdcall transmogrify(void* context) { static_cast<MeineKlasse*>(context)->elementfunktion(); } } ... MeineKlasse* p = ...; Threading::HTread t (&transmogrify, p, ...);Das Problem ist aber, dass ich nicht weiß wo ich transmogrify deklarieren soll? Innerhalb der Klasse? Da bekomme ich einen Compilerfehler. Tut mir leid, wenn ich mich gerade etwas doof anstelle.
-
Wenn ich mir anschaue, wie es in boost::thread gemacht wird:
struct ThreadProxyData { typedef unsigned (__stdcall* func)(void*); func start_address_; void* arglist_; ThreadProxyData(func start_address,void* arglist) : start_address_(start_address), arglist_(arglist) {} }; DWORD WINAPI ThreadProxy(LPVOID args) { ThreadProxyData* data=reinterpret_cast<ThreadProxyData*>(args); DWORD ret=data->start_address_(data->arglist_); delete data; return ret; } typedef void* uintptr_t; inline uintptr_t const _beginthreadex(void* security, unsigned stack_size, unsigned (__stdcall* start_address)(void*), void* arglist, unsigned initflag, unsigned* thrdaddr) { DWORD threadID; HANDLE hthread=CreateThread(static_cast<LPSECURITY_ATTRIBUTES>(security),stack_size,ThreadProxy, new ThreadProxyData(start_address,arglist),initflag,&threadID); if (hthread!=0) *thrdaddr=threadID; return reinterpret_cast<uintptr_t const>(hthread); }Stelle ich fest, dass hier doch nicht viel anders verfahren wird.
-
FrEEzE2046 schrieb:
Wenn du das hier meinst:
extern "C" { void __stdcall transmogrify(void* context) { static_cast<MeineKlasse*>(context)->elementfunktion(); } } ... MeineKlasse* p = ...; Threading::HTread t (&transmogrify, p, ...);Das Problem ist aber, dass ich nicht weiß wo ich transmogrify deklarieren soll? Innerhalb der Klasse?
Nein, das soll eine freie Funktion sein. Ich bin mir nur nicht sicher, ob die "__stdcall"-Syntax da richtig ist. Das ist eh kein Standard-C++...
Idealerweise versteckt man so etwas hinter der Thread-Bibliothek, wie es boost::thread tut. boost::thread akzeptiert beliebige "Funktoren" und setzt das intern wahrscheinlich mit "Type Erasure" um. Du hast aber nicht vor, eine eigene Thread-Bibliothek zu schreiben, oder? Das würde ich Spezialisten überlassen...
Gruß,
SP
-
nein natürlich nicht.
Ich halte die Vorgehensweise dennoch für schlauer, dass man auch normale Funktionen übergeben kann, die dann intern aufgerufen werden. Das macht alles ein wenig einfacher.
Ich bekomme jedoch bei der Zuweisung von "ret" eine Exception
unsigned long __stdcall ThreadProxy( void* args ) { ThreadProxyData* data = reinterpret_cast<ThreadProxyData*>(args); DWORD ret = data->start_address_(data->arglist_); delete data; return ret; }
-

Funktionen "castet" man nicht.Ich übergebe an den nächsten Hilfsbereiten. Meine Zeit für heute ist um.
-
Sebastian Pizer schrieb:

Funktionen "castet" man nicht.Ich meinte auch nicht die Funktion die gecastet wird. Hier wird doch einfach eine struct gecastet, die dann die Adresse der Funktion und deren Parameter enthält oder etwa nicht?
-
Hier ist ein letztes und vollständiges Beispiel, welches das Prinzip verdeutlichen soll, mit dem man Elementfunktionen aufrufen kann, obwohl man einen Zeiger auf eine freie Funktion übergeben muss. Der Schlüssel zur Lösung ist hier der void*-Parameter.
#include <iostream> typedef void thread_funktion_t(void*); void ruf_auf(thread_funktion_t* pf, void* kontext) { pf(kontext); } class MeineKlasse { public: void foo(); }; void MeineKlasse::foo() { std::cout << "Hallo!\n"; } void proxy_MeineKlasse_foo(void* kontext) { static_cast<MeineKlasse*>(kontext)->foo(); } int main() { MeineKlasse o; ruf_auf(proxy_MeineKlasse_foo,&o); }Wenn Du damit jetzt nichts anfangen kannst, solltest Du die Finger von Threads lassen.
-
Sebastian Pizer schrieb:
Wenn Du damit jetzt nichts anfangen kannst, solltest Du die Finger von Threads lassen.
Doch doch, dass ist mir doch auch klar. Die Frage geht eigentlich in eine andere Richtung mittlerweile.
Wie kann ich es anstellen, dass ich meiner Thread Klasse eine Funktion mit beliebiger Konvention übergebe, so wie es ja anscheinend boost::thread macht?
Ich meine, dass bekomme ich natürlich schon hin:typedef unsigned (__stdcall *func)(void*); func f = static_cast<func>(this->_lpFunction); this->_hThread = mybeginthreadex( NULL, 0, f, this->_lpParams, CREATE_SUSPENDED, 0 );aber wenn _lpFunction ein void* ist, wird logischerweise der Stack nicht korrekt bereinigt. Mich würde interessieren, wie man diese Problematik lösen kann.