According to C++11 Standard, stateless lambdas, i.e. having an empty lambda introducer or capture no variables, are implicitly convertible to function pointers. Visual C++ in Visual Studio 2012 and newer, supports this feature. Moreover, in Visual C++ stateless lambdas are convertible to function pointers with arbitrary calling conventions. This is great if have to deal with Win32 API functions, most of them using __stdcall calling convention.
Let’s begin with two simple examples of enumerating top-level windows, one by using a callback function and the other by using a lambda expression.
Enumerate windows using “classic” callback functions
Usually, for that purpose we declare and implement as callback, a static member function having the signature required by EnumWindows in its first argument.
class CDemoDlg : public CDialog { // ... static BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam); };
BOOL CALLBACK CDemoDlg::EnumWindowsProc(HWND hWnd, LPARAM lParam) { CDemoDlg* pDlg = reinterpret_cast<CDemoDlg*>(lParam); // do something, e.g. add the found window handle to a list return TRUE; // continue the enumeration } void CDemoDlg::OnButtonEnumWindows() { ::EnumWindows(&CDemoDlg::EnumWindowsProc, reinterpret_cast<LPARAM>(this)); }
Notes
- CALLBACK is defined in Windows SDK as __stdcall.
Enumerate windows using lambda expressions
The same result can be achieved by using a lambda expression. This is somehow simpler because does not require to declare and define a static member function.
void CDemoDlg::OnButtonEnumWindows() { // in the first parameter of EnumWindows, pass a lambda expression ::EnumWindows([](HWND hWnd, LPARAM lParam) -> BOOL { CDemoDlg* pDlg = reinterpret_cast<CDemoDlg*>(lParam); // do something, e.g. push the found window handle in a list. return TRUE; // continue the enumeration }, reinterpret_cast<LPARAM>(this)); // second parameter of EnumWindows }
Notes
- as stated in the introduction, there is not necessary to explicitly convert the lambda expression to the function pointer required by EnumWindowsProc (WNDENUMPROC);
- as stated in the introduction, there is not possible to capture neither local variables, nor this pointer; luckily, like in many other WinAPI callbacks, we can pass the necessary stuff in a parameter of type LPARAM or LPVOID.
Using nested lambda expressions
Let’s say we want to enumerate the top-level windows in a worker thread. We can write something like this:
void CDemoDlg::OnButtonEnumWindows() { AfxBeginThread([](LPVOID lpParam) -> UINT { ::EnumWindows([](HWND hWnd, LPARAM lParam) -> BOOL { CDemoDlg* pDlg = reinterpret_cast<CDemoDlg*>(lParam); // do something, e.g. push the found window handle in a list return TRUE; // continue the enumeration }, reinterpret_cast<LPARAM>(lpParam)); // second parameter of EnumWindows return 0; }, reinterpret_cast<LPVOID>(this)); // second parameter of AfxBeginThread }
We have passed lambdas both for callback parameter of AfxBeginThread and EnumWindows. EnumWindows is called in the first lambda body and takes a lambda as well.
This compiles with no problem in x64 builds, but may give a conversion error for the inner lambda in Win32 ones. That’s a little glitch which AFAIK has been fixed in Visual Studio 2015.
However, to be sure it compiles also in Visual Studio 2012/2013 – Win32 builds, we can simply cast explicitly the inner lambda.
void CDemoDlg::OnButtonEnumWindows() { AfxBeginThread([](LPVOID lpParam) -> UINT { ::EnumWindows(static_cast<WNDENUMPROC>([](HWND hWnd, LPARAM lParam) -> BOOL { CDemoDlg* pDlg = reinterpret_cast<CDemoDlg*>(lParam); // do something, e.g. push the found window handle in a list return TRUE; // continue the enumeration }), reinterpret_cast<LPARAM>(lpParam)); // second parameter of EnumWindows return 0; }, reinterpret_cast<LPVOID>(this)); // second parameter of AfxBeginThread }
Notes
- x64 builds has no __stdcall calling convention; __stdcall keyword is still accepted but simply ignored by the compiler.
- of course, we can use lambda expressions in the same way also in ATL applications or C++ programs that use plain WinAPI.
Resources and related articles
- ISO/IEC 14882 Third Edition: 5.1.2 Lambda expressions
- Visual C++ Team Blog: C++11 Features in Visual C++ 11
- MSDN: What’s New for Visual C++ in Visual Studio 2012
- Codexpert blog: Using Lambdas in MFC Applications – Part 1: Sorting Arrays
- Codexpert blog: C++11: Let’s Write a “Hello Lambda!”