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しよう。