Sind Threads _IN_ einer Klasse möglich? Wenn ja wie geht das?



  • Holla,
    wie der Titel/Betreff schon sagt, möchte ich gernen wissen ob man Threads in einer Klasse, zB. mit einer Methode Starten und Beenden kann. Zur Zeit arbeite ich an einer SOCKET Klasse und dort möchte ich gern the Threads zum empfangen und auswerten von Nachrichten einsetzten. Leider fehlt mir das Wissen dazu, wie und ob man mit Threads in einer Klasse arbeiten kann. Ich habe die Suchfunktion bereits genutzt, da ich mit Threads nicht auskenne verstand ich nahezu nichts.

    StYleZ



  • Ja. Du musst dazu die Methode "static" machen und den "this" Pointer als Variable übergeben.
    Dann in der "static"-Methode die eigentliche "Member-Funktion" (dazu hast Du ja den "this"-Pointer übergeben bekommen) aufrufen.



  • BTW: Eine static-Methode ist eigentlich falsch, es muss eine freie Funktion mit extern "C" sein.
    Aber da es auf keinem Compiler für Windows zu Problemen kommt kann man natürlich auch die static-Methode nehmen.



  • ----- schrieb:

    BTW: Eine static-Methode ist eigentlich falsch, es muss eine freie Funktion mit extern "C" sein.

    huch? kannst du das mal genauer darlegen?



  • Da static-Methoden keine vtable haben, passt es... eigentlich mit jedem Compiler... aber ob das im Standard so steht, wiess ich jetzt nicht... mit "extern "C"" kann es eigentlich nichts zu tun haben... eher mit der "calling-convention"; die ist aber je nach verwendeten "create" etwas anders...



  • da steht was dazu, aber nicht im detail: http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.2

    However, although it probably works on most compilers, it actually would have to be an extern "C" non-member function to be correct, since "C linkage" doesn't only cover things like name mangling, but also calling conventions, which might be different between C and C++.



  • Also, der Grund ist die Calling-Convention, oder? Und die kann man (zumindest bei MS VC++) bei static Memberfunktionen angeben...



  • Danke für die Antworten.
    Leider bin ich nicht wirklich schlau daraus geworden.
    Kann jemand ein simples/kleines Beispiel posten?

    StYleZ





  • Hi,
    ich komme nicht dahinter.
    Zurzeit bekomme ich folgende Fehlermeldung:

    [BCC32 Fehler] sock.cpp(154): E2034 Konvertierung von 'void (* (_closure )(void *))(void )' nach 'void ()(void )' nicht möglich
    [BCC32 Fehler] sock.cpp(154): E2342 Keine Übereinstimmung des Typs beim Parameter '__start' ('void (
    )(void *)' erwartet, 'void' erhalten)

    Ich nutze CodeGear C++ Builder.
    Hat jemand eine Anhung was man ändern mus, damit es läuft?
    Hier der komplette Code:

    sock.h

    #include <vcl.h>
    #include <winsock2.h>
    #include <process.h>
    
    class sock
    {        
      private:
    	bool tRun;
    	SOCKET socke; 			//FÜR
    	SOCKADDR_IN addr;   	//CLIENT
    	SOCKET acceptsocket;	//FÜR
    	SOCKET serversocket;    //SERVER
    	AnsiString type; 		//FÜR
    	AnsiString protokoll;   //BEIDE
    	AnsiString url;         //TYPEN
    	int port;
    	SOCKADDR_IN remoteAddr; //FÜR
    	int remoteAddrLen; 		//UDP
    	static int zaehler;
    	int wsastart();
    	AnsiString resolvehost(AnsiString serverurl);
    
      public:
    	sock();
    	~sock();
    	int snd(AnsiString sendbuff);
    	int rcv(AnsiString& recvbuff);
    	void init(AnsiString Stype,AnsiString Sprotokoll,AnsiString Surl,int Sport);
    	void StartThread();
    	void CloseThread();
    	void trcv(void *dummy);
    };
    

    sock.cpp

    int sock::zaehler = 0;
    sock::sock() {}
    sock::~sock()
    {
    	zaehler--;
    	if (lstrcmp(type.c_str(),"client")==0) { closesocket(socke); }
    	else {
    		if (lstrcmp(protokoll.c_str(),"tcp")==0) { closesocket(acceptsocket); }
    		closesocket(serversocket);
    	}
    	if (zaehler == 0) { WSACleanup(); }
    }
    void sock::init(AnsiString Stype,AnsiString Sprotokoll,AnsiString Surl,int Sport)
    {
    	tRun = 0;
    	port = Sport;
    	url = Surl;
    	zaehler++;
    	if (zaehler == 1) { wsastart(); }
    	if(lstrcmp(Stype.c_str(),"server")==0)		//Server
    	{
    		type = "server";
    		memset(&addr,0,sizeof(SOCKADDR_IN));
    		addr.sin_family=AF_INET;
    		addr.sin_port=htons(port);
    		addr.sin_addr.s_addr=ADDR_ANY;
    		if (lstrcmp(Sprotokoll.c_str(),"tcp")==0)
    		{
    			protokoll = "tcp";		//TCP
    			acceptsocket = socket(AF_INET, SOCK_STREAM, 0);
    			if (acceptsocket == INVALID_SOCKET) { cout<<"couldn't create socket (accept socket)\n"; }
    			else { cout<<"acceptsocket set to TCP\n"; }
    			if (bind(acceptsocket,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN)) == SOCKET_ERROR) { cout<<"couldn't bind socket\n"; }
    			else { cout<<"acceptsocket seccessfully bound (tcp server)!\n"; }
    			if (listen(acceptsocket,10) == SOCKET_ERROR) { cout<<"couldn't set listen mode\n"; }
    			else { cout<<"acceptsocket set to Listen mode\n"; }
    			serversocket=accept(acceptsocket,NULL,NULL);
    			if (serversocket == INVALID_SOCKET) { cout<<"couldn't accept connection\n"; }
    			else { cout<<"ServerSocket accepted a connection!\n"; }
    		}
    		else if (lstrcmp(Sprotokoll.c_str(),"udp")==0)
    		{
    			protokoll = "udp"; 		//UDP
    			remoteAddrLen=sizeof(SOCKADDR_IN);
    			serversocket = socket(AF_INET,SOCK_DGRAM,0);
    			if (serversocket == INVALID_SOCKET) { cout<<"couldn't create socket (serversocket UDP)\n"; }
    			else { cout<<"serversocket set to UDP\n";
    				if (bind(serversocket,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN)) == SOCKET_ERROR) { cout<<"couldn't bind socket\n"; }
    				else { cout<<"serversocket seccessfully bound (udp server)!\n"; }
    			}
    		}
    	}
    	else if (lstrcmp(Stype.c_str(),"client")==0)		//Client
    	{
    		type = "client";	//TCP
    		url = resolvehost(url);
    		memset(&addr,0,sizeof(SOCKADDR_IN));
    		addr.sin_family=AF_INET;
    		addr.sin_port=htons(port);
    		addr.sin_addr.s_addr=inet_addr(url.c_str());
    		if (lstrcmp(Sprotokoll.c_str(),"tcp")==0)
    		{
    			protokoll = "tcp";
    			socke = socket(AF_INET, SOCK_STREAM, 0);
    			if (socke == INVALID_SOCKET) { cout<<"couldn't create socket (socke)\n"; }
    			else { cout<<"socket set to tcp!\n"; }
    			if (connect(socke,(SOCKADDR*)&addr,sizeof(SOCKADDR)) == SOCKET_ERROR) { cout<<"couldn't connect\n"; }
    			else { cout<<"connected to server\n"; }
    		}
    		else if (lstrcmp(Sprotokoll.c_str(),"udp")==0)
    		{
    			protokoll = "udp";	//UDP
    			remoteAddrLen=sizeof(SOCKADDR_IN);
    			socke=socket(AF_INET,SOCK_DGRAM,0);
    			if (socke == INVALID_SOCKET) { cout<<"couldn't create socket (socke)\n"; }
    			else { cout<<"socket set to udp!\n"; }
    		}
    	}
    	else { cout<<"unknown type"<<endl; }
    }
    int sock::snd(AnsiString sendbuff)
    {
    	if (lstrcmp(type.c_str(),"client")==0) {
    		if (lstrcmp(protokoll.c_str(),"tcp")==0) { return send(socke,sendbuff.c_str(),sendbuff.Length(),0); }
    		else { return sendto(socke,sendbuff.c_str(),sendbuff.Length(),0,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN)); }
    	}
    	else {
    		if (lstrcmp(protokoll.c_str(),"tcp")==0) { send(serversocket,sendbuff.c_str(),sendbuff.Length(),0); }
    		else { return sendto(serversocket,sendbuff.c_str(),sendbuff.Length(),0,(SOCKADDR*)&remoteAddr,remoteAddrLen); }
    	}
    	return 0;
    }
    int sock::rcv(AnsiString& recvbuff)
    {
    	int fix;
    	char rcvbuff[10000];
    	if (lstrcmp(type.c_str(),"client")==0) {
    		if (lstrcmp(protokoll.c_str(),"tcp")==0) { fix = recv(socke,rcvbuff,10000,0); }
    		else { fix = recvfrom(socke,rcvbuff,10000,0,(SOCKADDR*)&remoteAddr,&remoteAddrLen); }
    	}
    	else {
    		if (lstrcmp(protokoll.c_str(),"tcp")==0) { fix = recv(serversocket,rcvbuff,10000,0); }
    		else { fix = recvfrom(serversocket,rcvbuff,10000,0,(SOCKADDR*)&remoteAddr,&remoteAddrLen); }
    	}
    	rcvbuff[fix]='\0';
    	recvbuff = rcvbuff;
    	return fix;
    }
    AnsiString sock::resolvehost(AnsiString serverurl)
    {
    	hostent *dns = gethostbyname(serverurl.c_str());
    	if (!dns) { return 0; }
    	else
    	{
    		sprintf(serverurl.c_str(), "%u.%u.%u.%u",
    			(unsigned char) dns->h_addr_list[0][0],
    			(unsigned char) dns->h_addr_list[0][1],
    			(unsigned char) dns->h_addr_list[0][2],
    			(unsigned char) dns->h_addr_list[0][3]);
    		return serverurl;
    	}
    }
    int sock::wsastart()
    {
    	WSADATA wsa;
    	return WSAStartup(MAKEWORD(2,2),&wsa);
    }
    void sock::trcv(void *dummy)
    {
    	while (tRun)
    	{
    		AnsiString recvbuff;
    		int fix;
    		char rcvbuff[10000];
    		if (lstrcmp(type.c_str(),"client")==0) {
    			if (lstrcmp(protokoll.c_str(),"tcp")==0) { fix = recv(socke,rcvbuff,10000,0); }
    			else { fix = recvfrom(socke,rcvbuff,10000,0,(SOCKADDR*)&remoteAddr,&remoteAddrLen); }
    		}
    		else {
    			if (lstrcmp(protokoll.c_str(),"tcp")==0) { fix = recv(serversocket,rcvbuff,10000,0); }
    			else { fix = recvfrom(serversocket,rcvbuff,10000,0,(SOCKADDR*)&remoteAddr,&remoteAddrLen); }
    		}
    		rcvbuff[fix]='\0';
    		recvbuff = rcvbuff;
    		cout<<recvbuff.c_str()<<endl;
    	}
    	if (tRun == 0) {
    		_endthreadex( 0 );
    	}
    }
    void sock::StartThread()
    {
    	tRun = 1;
    	_beginthread(trcv,0,NULL);
    }
    void sock::CloseThread()
    {
    	tRun = 0;
    }
    

    File.cpp

    #include <vcl.h>
    #include <iostream.h>
    #include <process.h>
    #include "sock.h"
    #include "sock.cpp"
    #pragma hdrstop
    #pragma argsused
    
    int main(int argc, char* argv[])
    {
    	AnsiString msg="";
    	sock s1;
    	s1.init("client","tcp","irc.quakenet.org",6667);
    	s1.StartThread();
    	AnsiString emailaddress = "blubb@bla.com",fullname = "bla";
    	AnsiString NICK = "NICK test123\r\n";
    	AnsiString USER = "USER " + emailaddress.SubString(1,emailaddress.Pos("@")-1) + ' ' + '"' + emailaddress.SubString(emailaddress.Pos("@")+1,emailaddress.Length()) + '"' + ' ' + '"' + "localhost" + '"' + ' ' + ':' + fullname + "\r\n";
    	s1.snd(NICK);
    	s1.snd(USER);
    	system("PAUSE");
    	return 0;
    }
    

    StYleZ



  • Du hast die Hilfestellungen der anderen nicht umgesetzt.

    void* sock::trcv(void*)
    

    müsste, so bin ich der Meinung nach kurzem Überfliegen dieses Codebatzens,

    static void* sock::trcv(void*)
    

    sein.

    denn _beginthread will ja eine Funktion haben und keine Memberfunktio.



  • Richtig ich hab's nicht umgesetzt, aber ich habe es versucht.
    Nun das Problem scheint gelöst zu sein (zumindest meckert der Compiler nicht).

    sock.h

    static void trcv(void*);
    

    sock.cpp

    void sock::trcv(void*) { .... }
    

    Wie es immer so ist kommt sofort das nächste Problem.

    [BCC32 Fehler] sock.cpp(130): E2231 Element sock::tRun kann nicht ohne ein Objekt verwendet werden
    [BCC32 Fehler] sock.cpp(135): E2231 Element sock::type kann nicht ohne ein Objekt verwendet werden
    [BCC32 Fehler] sock.cpp(136): E2231 Element sock::protokoll kann nicht ohne ein Objekt verwendet werden
    [BCC32 Fehler] sock.cpp(137): E2231 Element sock::socke kann nicht ohne ein Objekt verwendet werden
    [BCC32 Fehler] sock.cpp(137): E2031 Typumwandlung von 'sockaddr_in sock::*' nach 'sockaddr ' nicht zulässig
    [BCC32 Fehler] sock.cpp(137): E2034 Konvertierung von 'int sock::
    ' nach 'int ' nicht möglich
    [BCC32 Fehler] sock.cpp(137): E2342 Keine Übereinstimmung des Typs beim Parameter 'fromlen' ('int ' erwartet, 'int sock::' erhalten)
    [BCC32 Fehler] sock.cpp(140): E2231 Element sock::protokoll kann nicht ohne ein Objekt verwendet werden
    [BCC32 Fehler] sock.cpp(141): E2231 Element sock::serversocket kann nicht ohne ein Objekt verwendet werden
    [BCC32 Fehler] sock.cpp(141): E2031 Typumwandlung von 'sockaddr_in sock::
    ' nach 'sockaddr ' nicht zulässig
    [BCC32 Fehler] sock.cpp(141): E2034 Konvertierung von 'int sock::
    ' nach 'int *' nicht möglich
    [BCC32 Fehler] sock.cpp(141): E2342 Keine Übereinstimmung des Typs beim Parameter 'fromlen' ('int ' erwartet, 'int sock::' erhalten)
    [BCC32 Fehler] sock.cpp(147): E2231 Element sock::tRun kann nicht ohne ein Objekt verwendet werden

    Hab mich an diesen Link orientiert: http://forum.fachinformatiker.de/c-c/85081-problem-_beginthread-aufruf.html
    Den Code geändert:

    main.cpp

    sock s1;
    s1.init("client","tcp","irc.quakenet.org",6667);
    _beginthread(s1.trcv,0,NULL);
    

    Kommt leider wieder auf das selbe hinaus.

    Danke für die Antwort Xantus 😉

    StYleZ



  • StYleZ schrieb:

    [BCC32 Fehler] sock.cpp(130): E2231 Element sock::tRun kann nicht ohne ein Objekt verwendet werden
    [BCC32 Fehler] sock.cpp(135): E2231 Element sock::type kann nicht ohne ein Objekt verwendet werden
    [BCC32 Fehler] sock.cpp(136): E2231 Element sock::protokoll kann nicht ohne ein Objekt verwendet werden
    [BCC32 Fehler] sock.cpp(137): E2231 Element sock::socke kann nicht ohne ein
    

    Das liegt daran, dass jetzt die static Memberfunktion versucht, auf Member der Klasse zuzugreifen. Das funktioniert aber nicht, weil du in der static Memberfunction nicht an den this-Zeiger kommst. Wie viel Erfahrung hast du mit der Sprache?

    Du gehst jetzt folgermaßen vor:

    Du lagerst den code aus static void* sock::trcv(void*) nach void sock::trvc_impl() aus, eine Memberfunktion.
    bei _beginthreadex() übergibst du als argument an trcv den this-Zeiger (mit cast nach void*).

    die static-Funktion sollte dann so ungefähr aussehen:

    /* static */ void* sock::trcv(void* data)
    {
        if(data) {
            return reinterpret_cast<sock*>(data)->trcv_impl();
        }
        return 0;
    }
    


  • sock.h

    static void* trcv(void* data);
    	void trcv_impl();
    

    sock.cpp

    void* sock::trcv(void* data)
    {
        if(data) {
    		return reinterpret_cast<sock*>(data)->trcv_impl();
        }
    	return 0;
    }
    void sock::trcv_impl()
    {
     	while (1)
    	{
              ......
    	}
    }
    

    main.cpp

    _beginthread(s1.trcv,0,s1.trcv((void*) zeiger));
    

    So hab ich den Post verstanden.
    Natürlich klappt es so nicht.
    Da ich nicht nachvollziehen kann, was du mit dem this-Pointer meinst.
    Bzgl. der Erfahrung.
    3 Jahre C/C++/ASM Grundkenntnisse.



  • StYleZ schrieb:

    3 Jahre C/C++/ASM Grundkenntnisse.

    Ohne dich persönlich angreifen zu wollen (!!), aber hast du in der Zeit geschlafen? 😕

    trvc_impl() muss schon vom typ void* und nicht void sein, sonst kannst den Wert doch nicht in static trcv zurückgeben!

    Lies dir mal etwas über den this-Zeiger (google: c++ this zeiger, 1. Ergebnis) durch, ebenso etwas über Funktionszeiger (bzw. Callbacks).



  • Du lagerst den code aus static void* sock::trcv(void*) nach void sock::trvc_impl() aus, eine Memberfunktion.

    Meiner Meinung nach habe ich das auch so gemacht wie du es geschrieben hast.
    Bzgl. der Ausbildung habe ich im ersten Jahr geschlafen, weil so langweilig war. Im 2ten und 3ten Jahr war ich wach. Da die Zeiger nur sehr kurz angesprochen wurden weis ich dem entsprechend wenig über Zeiger. Und da Threads sowieso nicht zur Grundausbildung gehören weis ich über Threads noch weniger.
    Bevor ich weiter im Dunkeln rumirre kannst du nicht einfach diese kleinen Änderungen posten?
    Ehrlich gesagt ist es für mich ein Ratespiel wie ich die Funktion aufrufen soll etc.



  • Hab es selbst geschafft.
    StYlez


Log in to reply