range-based forはコンピューター将棋で使えるのか?

C++11で導入されたrange-based for、使ってみました。


C++11で導入されたrange-based forは、要するにbegin()とend()だけ定義してあれば、あとは

for(auto sq : Square)

のように書けるらしいという噂を聞きつけ、定義してみた。

Square begin(Square x) { return SQ_ZERO;}
Square end(Square x) { return SQ_NB;}

コンパイルが通らない。

for(auto sq : Square() )

こうか。

コンパイラはoperator *()が定義されていないとか言ってやがる。よくはわからんが、次のように展開されているということなのかな。

よろしい

Square operator*(Square sq) { return sq; }

こうだ。よし、コンパイルが通った。他のenumもまとめて登録したいのでマクロにしよう。

よし、定義が終わった。

for(auto sq : Square() )

ダサいな、これ。「Square()」だか「Square(笑)」だか知らんが、こんなところに()を書きたくないんだよ。

#define SQ Square()
#define COLOR Color()
#define FILE File()
#define RANK Rank()
#define PIECE Piece()

こうするか。

for(auto sq : SQ )

ちょっとマシになった気がする。

上のENABLE_RANGE_OPERATORS_ONのマクロでついでに
#define SQ Square()
みたいなものは定義して欲しいぞ。

うわ。コンパイルが通らない。そうか。C/C++のdefineは1 PASSなのか。いつの時代のプリプロセッサ、いまだに使ってんだよ。本当、何とかしてくれよ。

ともかく、enumに対してrange based forで回せるところまでは来た。これによる速度低下があるのかどうかは俺にはわからん。誰か試して、コメントが欲しい。

次にBitboardだ。こいつも

while(target)
{
auto sq = target.pop();

}

みたいなコードは書きあきた。

これからの時代、

for(auto sq : target)

と書きたい。

ええと、begin()とend()があればいいんだよな。beginは初期状態で、endはこれが空のbitboardになればいいから、bitboardの内部状態をiteratorだとみなすとしたら、まずこうだろ。

inline const Bitboard begin(const Bitboard& b) { return b; }
inline const Bitboard end(const Bitboard& b) { return ZERO_BB; }

operator*()がないとか言ってやがる。じゃあ、Bitboard classのなかに入れてやるか。

Square operator*() { return pop(); }

あと、operator !=()と++()がないとか言ってやがる。!=()はBitboardの比較ができればいいからBitboardクラスのをそのまま使えばいいわな。++()は知らん。こうだ。

void operator++() {}

おお、コンパイルが通った。

おお。ちゃんと先手の49金の移動できる升が表示された。面白い。

しかしよく考えてみると次のように内部的に変換されるわけよな。

for(auto it = goldEffect(BLACK,SQ_49); it!=ZERO_BB ; ++it)
{
auto sq = *it; // ここでpopされる。
}

++itのところは空だから問題ないとして、it!=ZERO_BBの比較、少し無駄なような気はする。Bitboardがゼロかどうかを判定するのは、こんな他のBitboard(ZERO_BB)と比較しなくとも、内部的に保持しているSSEレジスタの値がゼロであるかを調べるほうが速いはずで、要するに、ここはend()と比較するように展開して欲しいわけではなく、itがendであるかどうかは自分で書かせて欲しいわけだ。

つまりrange based forは次のように展開して欲しい。

for(auto it = begin(); !it.is_ended() ; ++it)
{
auto sq = *it;
}

operator!=()を

こうなっている定義を右辺値見ないようにこう変えてしまえばいいのかも知れないが。

bool operator != (const Bitboard& rhs) const { return this; }

これだと他のところでBitboard同士の比較が出来なくなってしまうしな…。

ちなみにBitboardからboolへの変換子は次のようにしてある。

operator bool() const { return !(_mm_testz_si128(m, _mm_set1_epi8(static_cast<char>(0xffu)))); }

実際のところ、Bitboardに関してはZERO_BBと比較したところで速度低下はほとんどないかも知れないが…よくわからないので誰か実験してコメントください。

それにしても、悩ましい。なんでrange based forにend()みたいなものが要るのだ…。これ設計した奴は、何故、必ずend()が事前に得られると思っているのだ。

while(b)
{
auto sq = b.pop();

}

これをforで書けるようにしたいだけなんだよ。なんで20年前からdefineマクロで出来ることがいまだにforで書けないんだよ。C++なんとか委員会はこの20年間、何やってたんだよ。せめて次のように展開して欲しかったなぁ…。これなら、end()みたいなものは必要ないしな。

よしよし。これくらい書いておけば、C++のエキスパートが「それ、XXXで出来るよ」とか速攻でコメントをくれるだろう。はやく来て!C++の偉い人!!

追記

追記

追加で記事を書きました。
→ 「やねうらおが本当に必要だったもの」は必要ではない

range-based forはコンピューター将棋で使えるのか?」への4件のフィードバック

  1. あんまり火種は注ぎたくないので穏便にお願いしたいのですけど、江添さんが代替え案をブログに書いておいででした。
    http://cpplover.blogspot.jp/2015/12/blog-post.html

    自分も整数を展開するためにわざわざクラス書いたことありますけど、毎度それをどっかから引っ張ってくるのは手間なのであきらめました。
    For文自体は大好きなのでもうちょっと手軽になんとか使いたいところです。

Yakitori へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です