2013-08-21 4 views
5

Wystarczy eksperymentowanie, ale zastanawiałem się, czy to możliwe, aby to działało kodu (jak w kompilacji):Zastosowanie CreateThread z lambda

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(); 
} 

mam ten dokuczliwy podejrzenie, że ponieważ średnia podpis na LPTHREAD_START_ROUTINE zwrotnego to jest DWORD WINAPI Callback(PVOID) Nie będę mógł tego skompilować bez dodanego (ale gramatycznie nielegalnego) tagu WINAPI. Mówiąc o tym, czym dokładnie są atrybuty WINAPI i (na przykład)? Nigdy tak naprawdę nie rozumiałem, dlaczego w pewnych okolicznościach można mieć wiele atrybutów funkcji.

+0

'main' jest wymagane, aby' int' był typem zwracanym. Będziesz także potrzebował lambda '__stdcall', co nie jest możliwe. Możesz go zawijać, aby uzyskać lambdę (lub cokolwiek), lub po prostu użyć ''. – chris

+7

Skoro oczywiście używasz C++ 11, dlaczego nie użyć po prostu 'std :: thread', który jest przenośny i dobrze integruje się z lambdas, zamiast specyficznego dla platformy' CreateThread'? – syam

+0

Dopiero zacząłem się uczyć. Ale przyjrzę się temu. Dzięki! – sircodesalot

Odpowiedz

11

W rzeczywistości jest to możliwe w przypadku Visual C++ 2012 i nowszych; zacytować Microsoft's list of C++ feature support:

Dodatkowo w Visual C++ w Visual Studio 2012, bezpaństwowe lambdas podlegają zamianie funkcjonować wskaźniki. ... udostępniliśmy statuny lambda zamiennego na wskaźniki funkcji, które mają arbitralne połączenia z konwencjami . Jest to ważne w przypadku korzystania z API, które oczekują rzeczy jak __stdcall wskaźników funkcji

Więc w Visual C++ 2012 można zrobić coś takiego:

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

Oznacza to, że można również użyć lambdy z innymi API funkcje, które oczekują funkcji wywołania zwrotnego (na przykład takie jak EnumWindows() i CreateDialogParam()).

+2

Ah hah! Długo zastanawiałem się, jak to będzie działało, biorąc pod uwagę, że ABI ABI wymaga '__stdcall' dla (większości) swoich punktów wejścia dla wątków (x64 tak naprawdę nie ma znaczenia). Dziękuję za wskazanie tego, ** + 1 ** –

2

Można dokonać rzeczy nieco mniej rozwlekły używając 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); 

Albo, dla uzależnionych, którzy pasty kopia ta odpowiedź później), to wszystko, czego potrzebujesz:

auto work = [](LPVOID data) -> DWORD { return 0; }; 
int thread_param = 42; 
auto thread = CreateThread(nullptr, 0, work, &thread_param, 0, nullptr); 
+0

kompilator mówi, że nie mogę rzucić lambda na LPTHREAD_START_ROUTINE – J3STER

0

przynajmniej w obecnych * wersjach Mingw64 można określić konwencję Wywołanie funkcji lambda, jak w []() WINAPI {}:

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

*) Testowany z i686-w64-mingw32-g ++ - win32 (GCC) 6.3.0 20170516. Wcześniejsza wersja również może działać.