Click here to Skip to main content
13,624,821 members
Rate this:
 
Please Sign up or sign in to vote.
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 2 days ago
Updated yesterday
v4
Comments
Richard MacCutchan 2 days ago
   
The error message is perfectly clear. And you probably need to make that function static.
AntiRix 2 days ago
   
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 2 days ago
   
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.
AntiRix 2 days ago
   
Still desperate for an answer.
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 1

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 :
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 :
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.
  Permalink  
v3
Comments
AntiRix 2 days ago
   
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 2 days ago
   
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.
AntiRix 2 days ago
   
BeginThread is not defined. Look at the original post, I updated it to my most recent code.
Rick York yesterday
   
You obviously did not read my response. This is the last time I waste my time like this.
AntiRix 2 days ago
   
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.
0x01AA 2 days ago
   
My opinion: Before you try using threads you should be safe with the Basics. Means among others, how to prevent from circular dependencies....
AntiRix 2 days ago
   
I'm going up update the question with the code I currently have. This has left me feeling so drained and fed up. I don't know of any way to deal with circular dependencies other than to prevent them in the first place.
Jochen Arndt 2 days ago
   
Countered the one vote with 5 because it is the answer:
The thread function must be static and passing a pointer to a class allows access to the class members which includes calling a non static version of the thread function.

A better method than the extended example is to still pass a pointer to the class instance and implement the additional parameters as (usually private) class members rather than passing a structure. These members can be then accesed by the non static thread function or using the class pointer in the static function.
Rick York yesterday
   
I sometimes do that to but I hesitate to add members to the class just for this purpose. I think it comes down to a matter of preference on this issue.

Thanks for the vote.
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 2

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;
	...
}
  Permalink  

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

  Print Answers RSS
Top Experts
Last 24hrsThis month


Advertise | Privacy | Cookies | Terms of Service
Web02-2016 | 2.8.180712.1 | Last Updated 14 Jul 2018
Copyright © CodeProject, 1999-2018
All Rights Reserved.
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100