The Problem
Virtually all thread libraries were designed to handle C functions, not C++ class member functions. A typical library provides an API function that takes a pointer to a callback function as a thread's execution code and invokes that callback function in a separate thread. The problem is that you can't create a thread that executes an object's member function in such thread libraries; you can only use plain functions. For this reason, the following code fails:
// a thread launching function of a typical thread library
int thr_create (void (*pf)(), void* prm, thread_t* pth);
#include "class1.h"
int func (void *param )
{
thread_t t1;
// the following call results in a compilation error:
// "Cannot convert 'void (class1::*)()' to 'void (*)()'"
return thr_create ( &class1::some_method, param, &t1);
}
The function thr_create() expects an address of a callback function, void* as an address of the argument list to be passed to the callback function, and a pointer to a thread_t variable (for more information on callback functions and function pointers, read this 10-minute solution).
The code above fails to compile because the first argument passed to thr_create() is a pointer to a member function of class1, not a pointer to an ordinary function. As you probably know, ordinary functions and member functions are two entirely different things. Even a brute-force cast won't do in this case. How can you solve this problem?
Solution 1: Use a Static Member Function
The first solution is to make the callback member function static. A static member function doesn't take an implicit this argument, as do ordinary member functions. Therefore, you can take its address and treat it as if it were a pointer to an ordinary function. If you need to access an object's data member from the static member function, pass the object's address explicitly as an argument of the static function, for example:
class Hack
{
private:
int x;
public:
int get_x();
static void func(Hack * pthis); // static member function
void func2(); // nonstatic member function
};
void Hack::func(Hack * pthis)
{
int y = pthis->get_x(); // access an object's data member
}
Although this solution works for most cases, sometimes you can't make the member function static, e.g., when the member function is virtual or if you're using a third-party class that you can't modify. Fortunately, there is a way to get around this, except that in this case you will have to work a little harder.
Solution 2: Handling a Nonstatic Member Function
Suppose you need to invoke the member function func2(), a nonstatic member function of class Hack, in a separate thread. Instead of directly passing the member function's address to thr_create(), declare an ordinary function that takes void *, let's call it intermediary():
void intermediary(void*);
Next, create a struct that packs a pointer to the member function in question and a pointer to the class object:
struct A
{
Hack * p;
void (Hack::*pmf)(); // a pointer to a member function
};
Create an instance of that struct and fill it with the addresses of the desired object and its member function (you can read more on pointers to member functions in this 10-minute solution) as follows:
A a; // struct instance
Hack h; // create an object
//fill struct
a.p = & h;
a.pmf = &Hack::func2; // take address of member function
Now back to the implementation of the intermediary() function:
void intermediary(void* ptr)
{
A* pa=static_cast < A* > (ptr); // cast p to A*
Hack* ph=pa->p; // extract address of Hack object from A
void (Hack::*pmf)()=pa->pmf; // extract ptr to member func
(ph->*pmf)(); // call the member function
}
Finally, pass the address of intermediary() to thr_create():
thr_create (intermediary, (void*) &a, &t1 );
thr_create() invokes the function intermediary()and passes A's address to it. intermediary() in turn unpacks the A struct from its pointer argument and invokes the desired member function. The extra level of indirection enables you to safely launch a member function in a separate thread even if your thread library doesn't support member functions. If you need to launch different member functions of different classes, you can turn the A struct into a class template and intermediary() into a function template. This way, the compiler will generate most of the boilerplate code for you.