for each 文
VC8からの拡張機能で生のC++においてもfor each文が使える。VC8まではコンテナに対してのみだったが、9からは配列に対しても可。知らなかった。
// in vc9 #include <iostream> template<typename T, unsigned long N> inline void for_each_print(T (&array)[N]) { for each (const T& n in array) { std::cout << n << std::endl; } } int main() { int i = 0; int ary[100]; for each (int& n in ary) { n = ++i; } for_each_print(ary); return 0; }
1 2 ... 99 100
上記例の場合、Releaseビルド時に出力されるアセンブルコードは以下のfor文とまったく同じだった。
int main() { 00241640 sub esp,190h int i = 0; 00241646 xor eax,eax 00241648 lea ecx,[esp] 0024164B jmp main+10h (241650h) 0024164D lea ecx,[ecx] int ary[100]; for each (int& n in ary) { n = ++i; 00241650 inc eax 00241651 mov dword ptr [ecx],eax 00241653 add ecx,4 00241656 cmp eax,64h 00241659 jl main+10h (241650h) }
// これと等価 for (int n=0; n<100; ++n) { ary[n] = ++i; }
もちろん、ポインタには使うことはできない。宗教上の理由からあまり使われないのだが良い機能だと思う。
蛇足
any_cast関数は第2引数を第1引数として渡された変数の型にキャストする。GetProcAddressなどを利用する際に、型を定義するのが面倒くさい時に使う。
#include <windows.h> #include <tchar.h> template <typename T> inline T any_cast(T, void* value) { return reinterpret_cast<T>(value); } int main() { int ret1 = any_cast(MessageBoxW, ::GetProcAddress(::GetModuleHandle(_T("user32.dll")), "MessageBoxW")) (0,0,0,0); return ret1; }
一見、第一引数の受け渡しが無駄な処理を発生させるように見えるが、Releaseビルドするとany_cast関数の呼び出しは綺麗に取り除かれる。
int main() { int ret1 = any_cast(MessageBoxW, ::GetProcAddress(::GetModuleHandle(_T("user32.dll")), "MessageBoxW")) (0,0,0,0); 00281000 push offset string "MessageBoxW" (28928Ch) 00281005 push offset string "user32.dll" (289298h) 0028100A call dword ptr [__imp__GetModuleHandleA@4 (288004h)] 00281010 push eax 00281011 call dword ptr [__imp__GetProcAddress@8 (288000h)] 00281017 push 0 00281019 push 0 0028101B push 0 0028101D push 0 0028101F call eax ; GetProcAddressの戻り値(eax)を使ってそのままcallしている return ret1; } 00281021 ret
一番重宝するのがAPIフックをして、フック関数内でオリジナル関数を呼び出すとき。
void* g_OriginalRoutine; //... BOOL DetourRoutine(...) { return ant_cast(DetourdRoutine, g_OriginalRoutine)(...); }
MessageBoxWのように変数として参照できる場合のみ利用できるものなので、リンカが参照できない場合は素直にtypedefしよう。