Pointer innerhalb Klasse auf Memberfunktion



  • 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.



  • FrEEzE2046 schrieb:

    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?

    Mit einer Kombination aus Templates und Laufzeitpolymorphie. Die Technik heißt dann "Type Erasure".



  • Sebastian Pizer schrieb:

    Mit einer Kombination aus Templates und Laufzeitpolymorphie. Die Technik heißt dann "Type Erasure".

    Da lohnt sich der Aufwand nicht wirklich, würde ich sagen.

    Ein Problem hat deine Lösung jedoch noch:
    1. Die Thread-Funktion selbst startet die eigentlich Funktion die durch den Thread ausgeführt werden soll und muss außerhalb der Klasse deklariert sein.
    2. Damit muss die eigentliche Thread-Funktion (innerhalb der Klasse) unbedingt public sein, damit ich sie aufrufen kann ...

    nicht unbedingt die eleganteste Lösung



  • FrEEzE2046 schrieb:

    Ein Problem hat deine Lösung jedoch noch:
    1. Die Thread-Funktion selbst startet die eigentlich Funktion die durch den Thread ausgeführt werden soll und muss außerhalb der Klasse deklariert sein.
    2. Damit muss die eigentliche Thread-Funktion (innerhalb der Klasse) unbedingt public sein, damit ich sie aufrufen kann ...

    Es war keine "Lösung" sondern ein Beispiel, welches Dir etwas verdeutlichen sollte.

    nicht unbedingt die eleganteste Lösung

    🙄



  • Sebastian Pizer schrieb:

    FrEEzE2046 schrieb:

    Ein Problem hat deine Lösung jedoch noch:
    1. Die Thread-Funktion selbst startet die eigentlich Funktion die durch den Thread ausgeführt werden soll und muss außerhalb der Klasse deklariert sein.
    2. Damit muss die eigentliche Thread-Funktion (innerhalb der Klasse) unbedingt public sein, damit ich sie aufrufen kann ...

    Es war keine "Lösung" sondern ein Beispiel, welches Dir etwas verdeutlichen sollte.

    nicht unbedingt die eleganteste Lösung

    🙄

    Ja, okay - aber soll das etwa heißen, dass es keine Lösung für das Problem gibt? Es wird doch wohl möglich sein einen Thread aus einer Klasse heraus zu starten


  • Mod

    Nichts hindert dich, eine statische Memberfunktion zu verwenden.



  • FrEEzE2046 schrieb:

    Ja, okay - aber soll das etwa heißen, dass es keine Lösung für das Problem gibt?

    Welches Problem? Gibt es noch eins?

    FrEEzE2046 schrieb:

    Es wird doch wohl möglich sein einen Thread aus einer Klasse heraus zu starten

    Falls Du damit meinst, einen Thread eine Elementfunktion ausführen zu lassen: Klar, das geht! Siehe boost::thread, boost::bind und als "type erasure"-Beispiel noch boost::function. Ich werde Dir das hier nicht vorkauen. Du hast genug Stichwörter bekommen. Recherchiere selbst. Es lohnt sich!

    Gruß,
    SP



  • Ich werde es mir anschauen. Ich habe es auf die schnelle erst ein mal so gelöst:

    template< typename THREAD_ROUTINE >
    struct ThreadProxyData
    {
    	THREAD_ROUTINE	 lpFunction;
    	THREAD_DATA		 lpParams;
        ThreadProxyData( THREAD_ROUTINE start_address, THREAD_DATA arglist) : 
    					 lpFunction(start_address), lpParams(arglist) {}
    };
    

    Diese struct wird jetzt dem Konstruktor übergeben. Insofern ist dort bereits festgelegt, wie die Funktion aussieht. Intern rufe ich dann eine andere Funktion des Typs unsigend (__stdcall *Func)(void*) auf, welche wiederum dann diese Funktion aufruft.

    Ist eine - denke ich - ganz gute Lösung.



  • Wie ich nun einen Zeiger auf eine Klassenmethode bekomme hat sich dadurch natürlich immer noch nicht geklärt. Ich löse das jetzt wie folgt:

    // Funktion außerhalb Klasse
    unsigned int __stdcall __callAccept(void* _instance)
    {
    	while( TRUE ) {
    		static_cast<HClasses::ServerSocket*>(_instance)->_accept( NULL );
    	}
    	return 0;
    }
    
    // innerhalb Klasse
    	this->_acceptThread = new Threading::HThread( __callAccept,
    		                                          this,
    												  HClasses::Threading::PREPARED,
    												  false                           );
    

    Naja, eine wenig schöne Lösung. Fällt jemandem eine elegantere Art ein?



  • Ein Zeiger auf eine nicht-statische Elementfunktion ist nicht kompatibel zu einem Zeiger auf eine freie Funktion. Es gibt auch keine Zeiger auf "gebundene" Elementfunktionen.

    Das, was Du jetzt geschrieben hast, ist in etwa das gleiche, was ich als erste Antwort in diesem Thread geschrieben habe. Es ist erstaunlich, wie lange es gedauert hat...

    Fällt mir etwas eleganteres ein? Naja, im Prinzip kommst Du um so etwas nicht herum. Man kann aber diesen Proxy-Schnickschnack hinter der Threading-Bibliothek verstecken. Das wär dann nach meinem Empfinden elegant. Das sage ich allerdings auch nicht zum ersten Mal. Und bevor ich mich weiter wiederhole, werde ich jetzt mal ganz direkt: Du demonstrierst Inkompetenz. Das allein ist keine Schande. Es ist noch kein Meister vom Himmel gefallen. Du bist aber nicht in der Lage gewesen, anhand der Hinweise und Tipps, die man Dir gab, Dir selbst zu helfen. Das ist eine sehr schlechte Voraussetzung zum Schreiben einer eigenen Threading-Bibliothek. Was erwartest Du? Dass man Dir hier eine Komplettlösung anbietet? Das kannst Du haben: Nimm boost::thread. Wenn Du etwas ähnliches selbst bauen willst, bitte. Du kannst dann eine Suchmaschine zu den Stichwörtern befragen, die hier gefallen sind und selbst eine Lösung erarbeiten.

    Gruß,
    SP


Anmelden zum Antworten