user2516685
user2516685

Reputation: 295

Thread call member function

I need to call a member function of a class through thread but I don't want to use Boost library binding concept and the class I have to use doesn't have any static function to help out. I tried to use STL mem_fun_ref but I received a compile time error.

 base b;
 handle = CreateThread(NULL, 0,  LPTHREAD_START_ROUTINE(bind2nd(mem_fun_ref(&base::show), &b)),           
                          NULL, NULL, &dword);

class B{
public:
  void show(){
    cout << "show";
  }
};

Upvotes: 0

Views: 453

Answers (2)

Iuri Covalisin
Iuri Covalisin

Reputation: 645

This is C WinAPI function, here's the declaration:

HANDLE WINAPI CreateThread(
  _In_opt_   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  _In_       SIZE_T dwStackSize,
  _In_       LPTHREAD_START_ROUTINE lpStartAddress,
  _In_opt_   LPVOID lpParameter,
  _In_       DWORD dwCreationFlags,
  _Out_opt_  LPDWORD lpThreadId
);

The problem is the LPTHREAD_START_ROUTINE type defines a pointer to callback function, and not C++ method that has "hidden" this parameter.

Here's good example how to bypass it: http://blogs.msdn.com/b/oldnewthing/archive/2004/01/09/49028.aspx

class SomeClass {
 ...
 static DWORD CALLBACK s_ThreadProc(LPVOID lpParameter)
 {
  return ((SomeClass*)lpParameter)->ThreadProc();
 }
 DWORD ThreadProc()
 {
  ... fun stuff ...
 }
};

Upvotes: 1

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153810

I don't know about CreateThread() but looks like a C-style interface to thread creation: you probably won't be able to pass through a function object. Most likely, the start function type is the moral equivalent of

extern "C" typedef void (*entry_funciont)(void*);

possibly with a few extra parameters and returning something different than void. You'll need a forwarding function to recover your function object for which you'll need to know the exact type of the function object passed to the CreateThread() function as the "user data" (I'd guess where you pass NULL right after you function object. You could use something along the lines of this:

struct entry_base {
    virtual ~entry_base() {}
    virtual void call() = 0;
};
template <typename Fun>
struct entry
    : entry_base {
    Fun fun;
    entry(Fun fun): d_fun(fun) {}
    void call() {
        this->d_fun();
        delete this;
    }
};
template <typename Fun>
entry_base* make_entry(Fun fun) {
    return new entry<Fun>(fun);
}

extern "C" void call_entry(void* entry) {
    static_cast<entry_base*>(entry)->call();
}

... which can then be used something like this:

base* b = ...; // you can't use a local object here!
handle = CreateThread(NULL, 0,
                      LPTHREAD_START_ROUTINE(&call_entry),
                      make_entry(bind2nd(mem_fun_ref(&base::show), &b)),           
                      NULL, &dword);

Note that I'm speculating somewhat about the interface to CreateThread(), i.e., the user data pointer may very well go into a different location. However, binding to a pointer to a local object is almost certainly not going to work: this object is likely to go out of scope while the thread is running and it would remove the basis for the object running.

Upvotes: 1

Related Questions