yohhoyの日記

技術的メモをしていきたい日記

Windows APIコールバックでのラムダ式の利用

Visual Studio 11(MSVC11) Betaにおいて、C++11ラムダ式Windows APIコールバック関数を組み合わせを試したのでメモ。

C++11仕様“変数キャプチャを伴わないラムダ式は関数ポインタに変換可能”を利用し、コールバック関数を指定するWindows API関数へラムダ式を渡すことができる。この言語仕様はMSVC11からサポートされた。(MSDNでは"Stateless lambdas"と呼称。)

#include <windows.h>
MyData myData;

EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL /*CALLBACK*/ {
  MyData *myData = reinterpret_cast<MyData*>(lParam);
  //...
  return TRUE;
}, reinterpret_cast<LPARAM>(&myData));

// なお、呼び出し規約CALLBACKを指定しても下記警告が出力されるだけ@VC++11Beta
// "warning C4229: 旧形式が使用されています : データの修飾子は無視されます。"

メモ:プロジェクト既定の呼び出し規約は__cdeclに対し、マクロCALLBACKは__stdcallと定義されるため、本来は呼び出し規約不一致となるはず。上記コードが通ることから、MSVC11独自仕様として「ラムダ式から関数ポインタへの変換」では関数ポインタ型にあわせて呼び出し規約が暗黙指定されたとみなすのかも?((EnumWindows関数の場合、第1引数はWNDENUMPROC型=BOOL (CALLBACK *)(HWND, LPARAM)となっている。))

// 下記コードもコンパイル可
WNDENUMPROC proc = [](HWND hwnd, LPARAM lParam) -> BOOL { /*...*/ };
EnumWindows(proc, 0);

2012-03-14追記:呼び出し規約(calling conversion)に関してMSDNに明記あり。

Lambdas
The Visual C++ in Visual Studio 11 Beta is even better than that, because we've made stateless lambdas convertible to function pointers with arbitrary calling conventions. This is important when you are using APIs that expect __stdcall function pointers and so on.

http://msdn.microsoft.com/en-us/library/hh567368%28v=vs.110%29.aspx


N3337 5.1.2/p6より引用。

6 The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

関連URL