Click here to Skip to main content
13,767,705 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 13-Jul-18 6:46am
Updated 14-Jul-18 1: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.
AntiRix 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.
AntiRix 13-Jul-18 14:48pm
   
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 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.
AntiRix 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.
AntiRix 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.
0x01AA 13-Jul-18 14:53pm
   
My opinion: Before you try using threads you should be safe with the Basics. Means among others, how to prevent from circular dependencies....
AntiRix 13-Jul-18 14:58pm
   
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 13-Jul-18 17:08pm
   
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 14-Jul-18 12:35pm
   
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  
Comments
AntiRix 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)

  Print Answers RSS
Top Experts
Last 24hrsThis month


Advertise | Privacy | Cookies | Terms of Service
Web01-2016 | 2.8.181116.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