Function pointer auf Member
-
Vlt solltest du std::function etc heranziehen. Die Dinger sind nämlich verdammt geil....
-
Hallo erneut,
leider komme ich nicht weiter. Ich würde gerne ein Objekt der Klasse MyClass an den thread übergeben. Das schaffe ich nicht. Deshalb wollte ich zunächst nur einen Integer übergeben. Doch der scheint durch den cast irgendwie auf 0 gesetzt zu warden. Warum?
class MyClass { public: MyClass() { m_bFlag = false; } void threadRead(int a) { std::cout << "jjjjj = " << a << std::endl; //bool tmp = m_bFlag; } // protected: bool m_bFlag; }; void trampoline(void* p, int a) { std::cout << "a = " << a << std::endl; static_cast< MyClass*> (p)->threadRead(a); } // foo ist definiert so: typedef void(*mymethod)(void *d); int main() { MyClass myclass; int a = 4; MyThread t1( (mymethod) trampoline , (void*) a ); t1.start(); t1.join(); std::cin.get(); return 0; }
-
Ich glaub nicht, dass man ein int nach (void*) casten sollten.
Benutz den Adress-Operator und vergiss den Cast.
-
Kannst du das mal als compilierbaren Code zeigen?
Die Schnittstelle ist entweder sehr merkwürdig oder wird von dir falsch benutzt. Vermutlich soll doch der Callback die Signatur
void*(*)(void*)haben* und der zweite Parameter des Konstruktors ist dann ein Zeiger auf das Datenobjekt* . Derzeit übergibst du aber eine Funktion, deren Signatur überhaupt nicht passt und das Datenobjekt direkt. In beiden Fällen setzt du hier ziemlich krasse Casts ein, damit das überhaupt compiliert*. Dabei hätte der Compiler ganz recht, dass das Unsinn wäre, da die Parameter nicht passen. Du sagst ihm aber, er solle das Maul halten. Und dann wunderst du dich, dass das nicht funktioniert.Die mit * gekennzeichneten Teile sind nur Spekulation, interpoliert aus dem üblichen Aussehen eines Threadinterfaces in C. Ohne genauen Code kann man das nicht wissen.
Vielleicht wäre std::thread etwas für dich? Das kommt wunderbar mit typischen C++-Konstrukten zurecht und leistet vermutlich genau das gleiche (wahrscheinlich sogar mehr und besser) als irgendein dahergelaufener pthread-Wrapper.
-
Danke euch. Anbei unten ein Minimalbeispiel.
Was ich erreichen möchte ist, dass ich 2 threads erstelle die als parameter in ihre worker-function ein Objekt der Klasse MyClass bekommen so dass diese dann gleichzeitig auf einen Member der Klasse zugreifen. Ich möchte hier einen data race erzeugen.Momentan scheitere ich an der Übergabe ein und desselben OBjekts an 1 thread.
Das ganze sollte die typdefinition von mymethod behalten und auch MyThread sollte wenn nur erweitert werden wenn möglich. Alles also eher C-style function pointer und kein std::function oder Dergleichen.
Geht das?
#include <iostream> #include <windows.h> typedef void(*mymethod)(void *data); typedef struct { void* data; mymethod run; } ThreadData; DWORD WINAPI func(LPVOID lpParam) { ThreadData* data = static_cast<ThreadData*>(lpParam); data->run(data->data); return 0; } //typedef HANDLE MyThread; class MyThread { public: MyThread(){ } MyThread(mymethod run, void *data) { data_.data = data; data_.run = run; } ~MyThread() { } mymethod getRunMethod() const { return data_.run; } void* getThreadData() const { return data_.data; } const HANDLE& getCurrentThread() const { return thread_; } void start() { thread_ = CreateThread( NULL, 0, func, static_cast<LPVOID>(&data_), 0, NULL); } private: ThreadData data_; HANDLE thread_; }; class MyClass { public: MyClass() { //m_bFlag = false; } void threadRead() { std::cout << "within threadRead()" << std::endl; //bool tmp = m_bFlag; } //bool m_bFlag; }; void trampoline(void* p) { static_cast< MyClass*> (p)->threadRead(); } int main() { MyClass myclass; MyThread t1( (mymethod) trampoline, NULL ); // Hier soll objekt von myclass übergeben werden t1.start(); std::cin.get(); return 0; }
-
Ufuiko schrieb:
Momentan scheitere ich an der Übergabe ein und desselben OBjekts an 1 thread.
Das bedeutet?
-
Und? Bei mir funktioniert
MyThread t1(trampoline, &myclass); // Hier soll objekt von myclass übergeben werdenwie erwartet. Ich sehe kein Problem.
-
Tatsächlich. Es geht ja.
Ich bräuchte eine genauere Erklärung wasvoid trampoline(void* p) { static_cast< MyClass* >( p )->foo(); }tut (intern).
So wie ich das verstehe übergebe ich als thread-function einen wrapper der
einen void-pointer (kann also also sein?) bekommt und der wird auf die klasse gecastet um dann die eigentliche klassen-methode aufzurufen.Stimmt das alles? Danke euch für eure Hilfe
-
Zusatz: warum wird dann überhaupt genau das Objekt der Klasse aufgerufen? Woher Also wie kommt das zweite Argument von t1(...) an trampoline?
-
Ja, das stimmt. An den Thread übergibst du einen Zeiger auf die Funktion
trampolineund einen Zeiger auf das Objektmyclass(in Form eines Zeigers auf void, weil C-Schnittstelle, statt C++-Schnittstelle). Der Thread ruft intern die übergebene Funktion mit dem übergebenen Argument auf, hier also die Funktiontrampolinemit einem Zeiger auf das Objektmyclass(in Form eines Zeigers auf void, weil C-Schnittstelle, statt C++-Schnittstelle). Da man hier weiß, dass dieser void-Zeiger, p, auf ein Objekt der KlasseMyClasszeigt (man hat es ja selber so programmiert), kann man diesen void-Zeiger auch gefahrlos wieder zurück casten in einen Zeiger auf ein Objekt der KlasseMyClass. Und diesen kann man dann dereferenzieren und beispielsweise die Memberfunktionfooaufrufen. Da der void-Zeiger auf das Objektmyclasszeigte, wird also effektivmyclass.foo()aufgerufen.
Klingt komplizierter als es eigentlich ist.Ich möchte noch einmal erwähnen, dass es in C++ auch ganz nativ Threads gibt. Mit einem richtigen C++-Interface, an das man direkt Funktionen und Objekte übergeben kann. Oder es gibt auch 3rd-Party Bibliotheken, die dies leisten, beispielsweise Boost::Thread (die eine große Inspiration für das Design der in C++ eingebauten Threads war). Oder man kann sich, wenn es unbedingt sein muss, selber Schnittstellen dieser Art bauen (Boost kocht schließlich auch nur mit Wasser), anstatt sich selber einen schlechten* Wrapper im C-Stil zu schreiben.
*: Ja, der hier gezeigte Wrapper ist schlecht. Was soll es bringen, eine Thread-Klasse zu erstellen, wenn man nicht einmal die Vorteile von Klassen benutzt? So wie es derzeit ist stellt die Klasse effektiv nur Aliase für die C-Threadfunktionen zur Verfügung, ohne jedwede Programmlogik (was man doch eigentlich erreichen möchte).