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.


  • Mod

    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 werden
    

    wie erwartet. Ich sehe kein Problem.



  • Tatsächlich. Es geht ja.
    Ich bräuchte eine genauere Erklärung was

    void 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?


  • Mod

    Ja, das stimmt. An den Thread übergibst du einen Zeiger auf die Funktion trampoline und einen Zeiger auf das Objekt myclass (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 Funktion trampoline mit einem Zeiger auf das Objekt myclass (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 Klasse MyClass zeigt (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 Klasse MyClass . Und diesen kann man dann dereferenzieren und beispielsweise die Memberfunktion foo aufrufen. Da der void-Zeiger auf das Objekt myclass zeigte, wird also effektiv myclass.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).


Anmelden zum Antworten