• Welcome to Valhalla Legends Archive.
 

Threads from non-static class member functions.

Started by EvilCheese, May 03, 2003, 12:31 PM

Previous topic - Next topic

EvilCheese

Here's an interesting little problem and workaround I came across yesterday. Might be useful to someone, and I have nowhere better to post it ;)

I was attempting to create a thread from a non-static member function of a class, starting the thread from within another member function of the same class.

In the most basic sense:



Class a
{
   public:
   void ThreadFunction();
   void SomeOtherFunc();
};

.....
.....

void a::ThreadFunction()
{
  //Thread code which accesses non static data members
}

void a::SomeOtherFunc()
{
       //Some code
.....
.....
       CreateThread(NULL, NULL &ThreadFunction, NULL, NULL, NULL);
}



This wont compile (different messages on different compilers).

The reason for this is that every class member funtion is passed an invisible pointer to the current instance of the class, allowing it to know which instance it belongs to, and to interact with other members.

To get around this, I came up with the following neat (but hacky) little solution.

I created a flat  (C) function which wrapped the thread creation and passed the necessary (this) pointer to the member function. A little like this:



unsigned long _stdcall threadwrapper (void * classptr)
{
  //Now cast the classptr pointer, and use it to call the thread function.
  ((a*) classptr)->ThreadFuntion();
  return somevalue;
}

Class a
{
   public:
   void ThreadFunction();
   void SomeOtherFunc();
};

.....
.....

void a::ThreadFunction()
{
  //Thread code which accesses non static data members
}

void a::SomeOtherFunc()
{
       //Some code
.....
.....
       CreateThread(NULL, NULL &threadwrapper, this, NULL, NULL);
}



If you want to pass other arguments to your thread function, you can wrap them along with the "this" pointer inside an argument structure and then pass and extract the whole thing inside your wrapper function.

Hope this helps someone out. No idea who, but you never know :)

Skywing

That's a pretty standard way of doing things - it's exactly what you're supposed to use that pointer-sized callback parameter for.

Other common uses include window procedures and the like.

By the way, are you aware that using CRT functions with CreateThread results in memory leaks?

Oh, and you need to have class lowercased; Class is a syntax error.

EvilCheese

I'd edit the code to get rid of the caps if I could (I wrote it in the window, only skim-read it, and I have a bad caps addiction :P)

I had no idea that was a common way of doing things. Most often when I've asked the question "How do you set up a callback or thread based on a class member function?" I've been told "You dont unless it's static." so I figured it wasnt totally common knowledge.

As for the memory leaks, yes I'm aware of it, which is why I'm using _beginthread for my own purposes, I just thought CreateThread would be a more commonly understood and known API function.

Thanks for your concern though :)

Yoni

#3
Instead of making the thread proc a global C function, it's common to make it a static member function of your class. This way it fits better in the concept of object oriented programming.
Note that since C++ allows function overloading, the static and non-static member functions may have the same name.
Example:
class Fun
{
private:
   static void __cdecl ThreadProc(void* This);
   void ThreadProc();
public:
   void SomeFunction();
};

void __cdecl Fun::ThreadProc(void* This)
{
   static_cast<Fun*>(This)->ThreadProc();
}

void Fun::ThreadProc()
{
   // "Real" thread proc
}

void Fun::SomeFunction()
{
   _beginthread(&Fun::ThreadProc, 0, this);
}

Note: You shouldn't pass NULL as the last parameter of CreateThread, because it'll crash in Win9x.

EvilCheese

Thank you for the advice Yoni.

As a question, how far does the resulting code generated differ using your method in terms of size/efficiency, if at all?

Skywing

Quote from: EvilCheese on May 03, 2003, 04:19 PM
Thank you for the advice Yoni.

As a question, how far does the resulting code generated differ using your method in terms of size/efficiency, if at all?
Those two will produce identical output on all compilers that I know of.