FOREACHマクロ

目的

r を Range (配列やコンテナの一般化)とするとき,

// r の要素を各々表示する
// it はイテレータの名前
CRY_FOREACH(it, r){
  cout << *it << endl;
}

という構文を C++ 標準の範囲内の実装で可能にしたい.Boost.Foreach があるが,いくつかの点で気に入らない.

結果

VC7.1 で operator* を使うことがどうしてもできなかった(これの回避方法がなかった.これも参照).代わりに

CRY_FOREACH(it, r){
  cout << deref(it) << endl;
}

という構文なら可能になった("deref"は dereference の意味のつもり.operator* と全く同等).VC7.1 と GCC3.4.2 でコンパイル確認.要 Boost1.33.0.

使用しているテクニックのうち比較的マイナーなもののてきとーな説明

Friend Name Injection

クラステンプレート中で friend 関数が定義されている場合,クラステンプレートのインスタンス化に伴ってその friend 関数が名前空間に導入されるという規則.(14.5.3/5) や C++ Templates: The Complete Guide 参照.

template<class T>
struct injector{
  friend void f(T){}
};

injector<int> ii; // この瞬間に void f(int) が定義される.
injector<double> id; // 上と同じくここで void f(double) が定義される.
sizeof hack

オーバーロードされた関数の関数呼び出し構文に対して sizeof 演算子を適用して色々やる手法.

struct A{};
struct B{};
char (&test(A))[1];
char (&test(B))[2];

sizeof(test(A())); // 評価は"1"
sizeof(test(B())); // 評価は"2"
sizeof 演算子によるクラステンプレートのインスタンス化強制
template<class T>
struct A{};

sizeof(A<int>); // A<int> のインスタンス化を強制
鎖状継承とオーバーロードの優先順位

鎖状の継承関係 C0 <- C1 <- ... <- Cn があって,C0, C1, ...に対してオーバーロードされた関数がある場合,その関数の Cn による呼び出しは Cn に最も近いクラスのオーバーロードが呼ばれる,という規則.(13.3.3.2/4)

struct A{};
struct B : public A{};
struct C : public B{};

void f(A); // #1
void f(B); // #2

f(C()); // #2 が呼ばれる
派生クラスの一時オブジェクトの,基底クラス const 参照による延命

派生クラスの一時オブジェクトを基底クラスの const 参照で束縛し,一時オブジェクトの寿命を引き伸ばす手法.ScopeGuard の実装や Boost.Foreach の実装を参考にしたもの.

struct B{};
struct D : public B{};

B const &b = D(); // D() で生成された一時オブジェクトが b の寿命まで延命される.
多段 if 文 + if の条件式中で bool に変換可能なオブジェクトの宣言 (6.4/2)

Boost.Foreach の実装を参考にしたもの.

struct A
{
  operator bool()
  { return false; }
};

if(A a0); // 型 A のオブジェクトがこの位置で宣言可能.
else if(A a1);
else if(A a2);
else{
a1, a2, a3 はこのスコープに限定して導入される
}

これで if から最後の else までをマクロにすれば,スコープを限定したオブジェクトを複数生成しながら自然な構文を提供するマクロを書ける.

経緯

実装はコンパイル時グローバルカウンタの実装方法を思いついてから Boost.Foreach の実装などを参考にすぐに完成したけれど,それからかれこれ5ヶ月ぐらい VC7.1 で operator* を使う方法がどうしても実現できずにいて,結局諦めた.誰かボスケテ
以下,自分のブログへのトラバ飛ばし.
http://d.hatena.ne.jp/Cryolite/20040608#p1
http://d.hatena.ne.jp/Cryolite/20050502#p1
http://d.hatena.ne.jp/Cryolite/20050623#p2

効率

VC7.1 のリリースビルドでアセンブリ吐かせた感じでは,対応する手書きのループとほとんど同質のコードになる.

欠点

  • 翻訳単位あたりで使える回数に制限がある.下の実装は 100 回まで.増やせるけれど.
  • ごく一部に C++ 標準かどうか不明なところがある.
  • 古いコンパイラだと多分動かない気がする.知らんけど.