3

What's the difference between a class function and global function with regard to function pointers in C++? I'm asking as the Windows CreateThread method doesn't seem to accept the function that the thread code goes in if the function is a class member.

I can pass the function (that the thread code goes in) to the CreateThread message when it is a global method, but once I make it a member of a class I get the error "argument of type [method layout] is incompatible with parameter of type LPTHREAD_START_ROUTINE". ClassName::* is in the middle now; is this affecting it?

What is the way around this?

Ghost
  • 103
  • 4
  • 1
    lambdas in C++11 or boost::bind before that - post the offending code and we'll show you how – doctorlove Aug 14 '13 at 10:46
  • The signature of the possible thread-functions must be matched exactly, the Win32-API is a pure C API, not intended to work with C++-classes. – bash.d Aug 14 '13 at 10:47
  • @doctorlove, The result won't be compatible with the function pointer taken by `CreateThread`. – chris Aug 14 '13 at 10:47
  • 1
    In C++11, you might consider switching to the standard threading library. Then it's something along the lines of `thread([this]{member();})`, rather than the gibberish in the correct answer, with portability as a bonus. – Mike Seymour Aug 14 '13 at 11:28
  • @MikeSeymour, Good point, I don't know why that didn't cross my mind. I'll give that a prominent spot. – chris Aug 14 '13 at 12:07

2 Answers2

5

Member function pointers (DWORD(WINAPI Foo::*)(LPVOID)) are different types than function pointers (DWORD(WINAPI *)(LPVOID)). Member functions have a hidden this parameter, causing a signature mismatch.

The easiest way to do this is to use C++11's <thread> header:

struct Foo {
    void threadProc() {}
};

int main() {
    Foo foo;
    std::thread t{&Foo::threadProc, foo, /*other arguments to threadProc*/};
    t.join();
}

If you have to resort to CreateThread, make use of the void * parameter to pass the instance:

struct Foo {
    DWORD threadProc() {...}
};

extern "C" DWORD WINAPI proxyThreadProc(LPVOID userData) {
    auto foo = static_cast<Foo *>(userData);
    if (foo) {foo->threadProc();}
}

int main() {
    Foo foo;
    CreateThread(..., proxyThreadProc, &foo, ...);
}

The one in your class can now be pretty much whatever you want (like a std::function) and still work, as long as it's called with the right arguments from within the proxy procedure.

chris
  • 60,560
  • 13
  • 143
  • 205
  • 1
    It is worth noting that this would also work if `proxyThreadProc` were a static member function of `Foo`. This is sometimes a decent way to be able to use private member functions in such situations. – Magnus Hoff Aug 14 '13 at 11:08
  • Shouldn't 'extern "C"' be followed by a '{' and a closing '}' after the function? – Kevin Aug 14 '13 at 11:08
  • @MagnusHoff, It's kind of new for me as well, but [read this](http://stackoverflow.com/questions/1738313/c-using-class-method-as-a-function-pointer-type). – chris Aug 14 '13 at 11:09
  • 1
    @Kevin, The braces are only necessary for enclosing multiple. – chris Aug 14 '13 at 11:09
  • @chris Oh. Thanks. Relevant quote: "Note that a C callback function implemented in C++ must be `extern "C"`. It may seem to work as a static function in a class because class-static functions often use the same calling convention as a C function. However, doing that is a bug waiting to happen (see comments below), so please don't - go through an `extern "C"` wrapper instead." ([Interesting discussion on the topic](http://stackoverflow.com/questions/14395192/why-does-c11-not-support-declaring-extern-c-on-a-static-member-function)) – Magnus Hoff Aug 14 '13 at 11:14
  • @MagnusHoff, Yeah, and in reality, on Windows, I believe the likelihood of this ever being a problem is slim to none (which is probably why it evaded me knowing about it for so long), but better to have more correct code. – chris Aug 14 '13 at 11:16
0

yeah, as @chris said there is a hidden pointer of this which will be connected by the end of parameters. when the thread carry out that, it don't know to match with a pointer in the position of the last parameter, then it failed to recover the heap of this function when finished, so It's banned to use non-static member function of class to drive a thread function except for the global function or static member function of class.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
DarkHorse
  • 161
  • 1
  • 12