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

Visual Studio 11 BetaのVariadic Templatesエセ対応

Visual Studio 11(MSVC11) Betaの可変長引数テンプレート(Variadic Templates)対応*1についてメモ。

結論:言語機能としてはサポート一切なし。ただしC++標準ライブラリの一部をマクロでエミュレーション。MSVC12(?)に期待。

MSVC11 C++ライブラリの既定では最大5引数までサポート。マクロ_VARIADIC_MAXを指定することで最大10引数まで伸ばせる。

Simulated variadic templates now accept a maximum of 5 arguments by default, down from 10. To increase this limit, at the cost of compiler speed, define _VARIADIC_MAX project-wide between 5 and 10 inclusive.

http://blogs.msdn.com/b/vcblog/archive/2012/02/29/10272778.aspx

詳細は下記 Visual C++ Team Blog - C++11 Features in Visual C++ 11 に詳しい。(下線部は強調)

Faux variadics: We've developed a new scheme for simulating variadic templates. Previously in VC9 SP1 and VC10, we repeatedly included subheaders with macros defined differently each time, in order to stamp out overloads for 0, 1, 2, 3, etc. arguments. (snip) In VC11, the subheaders are gone. Now we define variadic templates themselves as macros (with lots of backslash-continuations), then expand them with master macros.(snip)In VC9 SP1 and VC10, infinity was 10 (i.e. "variadic" templates supported 0 to 10 arguments inclusive). In the VC11 Developer Preview, infinity is 5 by default.

http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx

2012-11-29追記:Announcing November CTP of the C++ compiler, now with more C++11 - Visual C++ Team Blogにて、"November 2012 Compiler CTP"でC++コンパイラのみ可変長引数テンプレートに対応した旨がアナウンスされた。このCTPはC++標準ライブラリの更新を含まない。あくまでCTP(Customer Technology Preview)版のため、ライセンスの扱いに注意。

*1:これを"対応"と言っていいものか…