Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use CreateThread with a lambda

Tags:

c++

winapi

Just experimenting, but I was wondering if it's possible to make this code work (as in compile):

void main() {
    int number = 5;

    DWORD(*dontThreadOnMe)(PVOID) = [](PVOID data) {
        int value = *(int*) data;

        cout << value << endl;
        cout << "This callback executed successsfully" << endl;
    };

    CreateThread(NULL, NULL, dontThreadOnMe, &number, NULL, NULL);
    cin.get();
}

I have this nagging suspicion that because the standard signature for a LPTHREAD_START_ROUTINE callback is DWORD WINAPI Callback(PVOID) I won't be able to get this to compile without the added (but grammatically illegal) WINAPI tag. Speaking of which, what exactly are the WINAPI and CALLBACK (for say WndProc) attributes? I've never really understood why in certain circumstances you could have multiple attributes on a function.

like image 550
sircodesalot Avatar asked Aug 21 '13 21:08

sircodesalot


3 Answers

Actually this is possible with Visual C++ 2012 and above; to quote from Microsoft's list of C++ feature support:

Additionally in Visual C++ in Visual Studio 2012, stateless lambdas are convertible to function pointers. ... we've made stateless lambdas convertible to function pointers that have arbitrary calling conventions. This is important when you are using APIs that expect things like __stdcall function pointers

So in Visual C++ 2012 you can do something like:

unsigned int id;
HANDLE hThread = reinterpret_cast<HANDLE>(_beginthreadex(0, 0,
    [](void* pData) -> unsigned int {
        // I'm a thread!
        return 0;
    }, pThreadData, 0, &id));

This means you can also use lambdas with other API functions that expect callback functions (things like EnumWindows() and CreateDialogParam(), for example).

like image 144
Jonathan Potter Avatar answered Nov 20 '22 13:11

Jonathan Potter


You can make things a little less wordy using auto :)

auto dontThreadOnMe = [](LPVOID data) -> DWORD {
    int value = *(int*)data;
    std::cout << value << std::endl;
    std::cout << "This callback executed successsfully" << std::endl;
    return 0; //don't forget your return code!
};

int number = 42;
auto thread = CreateThread(nullptr, 0, dontThreadOnMe, &number, 0, nullptr);

Or, for the copy paste addicts who find this answer later on ;), this is all you need:

auto work = [](LPVOID data) -> DWORD { return 0; };
int thread_param = 42;
auto thread = CreateThread(nullptr, 0, work, &thread_param, 0, nullptr);
like image 38
Carl Avatar answered Nov 20 '22 12:11

Carl


At least in current* versions of Mingw64 you can specify the calling convention of a lambda function, as in []() WINAPI {}:

CreateThread(
    nullptr, // lpThreadAttributes
    0,       // dwStackSize
    [](void *param) WINAPI -> DWORD { // lpStartAddress
        (void) param;
        return 0;
    },
    nullptr, // lpParameter
    0,       // dwCreationFlags
    nullptr  // lpThreadId
);

*) Tested with i686-w64-mingw32-g++-win32 (GCC) 6.3.0 20170516. Earlier version may work, too.

like image 2
kay Avatar answered Nov 20 '22 12:11

kay