Click here to Skip to main content
15,885,914 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi,

All I want to do is start a thread in another class. I've tried so many combinations of things but there's always an issue.

PlayerConnection.cpp:
#include "stdafx.h"
#include "PlayerConnection.h"

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <winsock.h>
#include <vector>

typedef struct
{
	PlayerConnection* instance;
	SOCKET& socket;
} ThreadData;

int PlayerConnection::ThreadMethod(SOCKET& socket)
{// recv etc
	return 0;
	
}

void PlayerConnection::init(SOCKET& socket)
{
	this->socket = socket;

	ThreadData* td;
	td->instance = this;
	td->socket = socket;

	DWORD connection_thread;

	CreateThread(NULL, 0, ThreadProcedure, &td, 0, &connection_thread);
}

int PlayerConnection::ThreadProcedure(LPVOID p)
{
	ThreadData* ptd = (ThreadData*)p;
	return ptd->instance->ThreadMethod(ptd->socket);
}

PlayerConnection.h:
#pragma once

#include <winsock.h>

class PlayerConnection
{
public:
	void init(SOCKET& socket);
	int ThreadMethod(SOCKET& socket);
	int ThreadProcedure(LPVOID p);

private:
	SOCKET socket;
	
};

Issues:

CreateThread(NULL, 0, ThreadProcedure, &td, 0, &connection_thread);

argument of type "int (PlayerConnection::*)(LPVOID p)" is incompatible with parameter of type "LPTHREAD_START_ROUTINE"
and
'PlayerConnection::ThreadProcedure': non-standard syntax; use '&' to create a pointer to member

What I have tried:

-------------------------------------------------------------
Posted
Updated 14-Jul-18 0:21am
v4
Comments
Richard MacCutchan 13-Jul-18 12:11pm    
The error message is perfectly clear. And you probably need to make that function static.
[no name] 13-Jul-18 12:17pm    
If I make that function static, it can't access any of the member variables. I can't then pass the class instance to that method, because it only accepts an LPVOID and PlayerConnection can't be cast to LPVOID.

It's not clear, because if it was, prepending & would fix it, but it doesn't, it then becomes

HANDLE CreateThread(LPSECURITY_ATTRIBUTES,SIZE_T,LPTHREAD_START_ROUTINE,LPVOID,DWORD,LPDWORD)': cannot convert argument 3 from 'DWORD (__stdcall *)(PlayerConnection)' to 'LPTHREAD_START_ROUTINE
Richard MacCutchan 13-Jul-18 12:26pm    
That is true, but you cannot pass the address of a class member function, since it cannot be calculated at compile time. And any pointer can be cast to LPVOID, as long as you understand the potential issues.
[no name] 13-Jul-18 14:48pm    
Still desperate for an answer.

What I usually do in this situation is as Richard mentioned. I use a static thread interface method as the thread procedure. A make another non-static method that creates the thread and passes this as its argument. Then it calls another non-static method that does the actual work. Here is a brief example with just the relevant bits :
C++
class CMyObject
{
    int ThreadMethod();   // do the work of the thread in this

    static int ThreadProcedure( pvoid * p )
    {
       CMyObject * pthis = (CMyObject *)p;
       return pthis->ThreadMethod();
    }

    void StartWorkThread()
    {
        // probably have to do some casting here to compile

        BeginThread( ThreadProcedure, this );  // change this to create your thread 
    }
};
Sometimes the thread method needs some additional arguments so I make a little class or structure to hold the arguments along with the this pointer since you can pass only one thing to the thread procedure. Here's an example of that :
C++
typedef struct
{
   int arg1;
   double arg2;
   CSomethingElse * pse;
   CMyObject * pthis;
} ThreadData;

int CMyObject::ThreadMethod( ThreadData *ptd )
{
   // this was changed to accept the data argument

   ThreadData td;
   memcpy( &td, ptd, sizeof( ThreadData ) );   // make a copy

  // and so on....
}

void CMyObject::StartWorkThread()
{
   ThreadData td;
   td.arg1 = 42;
   td.arg2 = 3.1416;
   td.pse = nullptr;
   td.pthis = this;

   BeginThread( ThreadProcedure, &td );  // change this to create your thread
}

int CMyObject::ThreadProcedure( pvoid *p )
{
   ThreadData * ptd = (ThreadData *)p;
   return ptd->pthis->ThreadMethod( ptd );
}
You can pass the thread data to the ThreadMethod also. The copy is made because the thread data will be invalid when StartWorkThread returns.
 
Share this answer
 
v3
Comments
[no name] 13-Jul-18 13:45pm    
Did you test that?

typedef struct
{
PlayerConnection* instance;
SOCKET& socket;
} ThreadData;

void PlayerConnection::init(SOCKET& socket)
{
this->socket = socket;

ThreadData data;
data.instance = this;
data.socket = socket;
CreateThread(receive_thread, (LPVOID)data);
}

Error on receive_thread: argument of type "unsigned int (*)(ThreadData data)" is incompatible with parameter of type "LPSECURITY_ATTRIBUTES"
Error on ThreadData data: the default constructor of "ThreadData" cannot be referenced -- it is a deleted function
Error on (LPVOID)data: no suitable conversion function from "ThreadData" to "LPVOID" exists

Even if I change it to ThreadData* data, Too few arguments in function call for CreateThread.
Rick York 13-Jul-18 16:11pm    
No, I did not test this because I do it ALL THE TIME. It works. Did you read the comment in there? It says, "probably have to do some casting here to compile." I have little function that simplifies that stuff for me which is common for those deal with threads A LOT.

If you are going to pass the data in it has to be done with a pointer so that means you need the & operator to get the address of it. Beyond that, give the call to CreateThread the appropriate arguments and you should be almost there. The mistake I made was my function is called BeginThread and I should have used that. It deals with the security attributes and stack size. I will update my post to use that.
[no name] 13-Jul-18 16:15pm    
BeginThread is not defined. Look at the original post, I updated it to my most recent code.
Rick York 14-Jul-18 12:36pm    
You obviously did not read my response. This is the last time I waste my time like this.
[no name] 13-Jul-18 14:09pm    
On top of that mess, I can't even use a .h file now, because I need to declare ThreadData in the PlayerConnection.h, but it contains a member of type PlayerConnection*. If I move ThreadData to another file, there's a circular dependency because it needs playerconnection but that needs threaddata.h.
Fixed using std::thread instead. Far neater, more modern, and somehow solves this issue. Not entirely sure how, but it does the job.

struct ThreadData
{
	PlayerConnection* instance;
	SOCKET& socket;
};
ThreadData td = { this, socket };
std::thread t(receive, td);
t.join();
void receive(ThreadData data)
{
	PlayerConnection* _this = data.instance;
	SOCKET& socket = data.socket;
	...
}
 
Share this answer
 
Comments
[no name] 16-Jul-18 18:18pm    
What a pathetic child you are. This solved my problem, you're in no position to downvote it.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900