C/C++ の switch - case
には、必ずしも case
を switch
直下に置く必要がないという、他の言語にはあまり継承されていない特徴がある。この用法をなんとなく集めてみた。
Wikipedia/Duff's Device (T. Duff)
この話で真っ先に例として挙げられるのがこれ。ループアンロールされた配列コピーのコード。
void send(int* to, int* from, size_t count) { size_t n = (count + 7) / 8; switch (count % 8) { do { case 0: *to++ = *from++; case 7: *to++ = *from++; case 6: *to++ = *from++; case 5: *to++ = *from++; case 4: *to++ = *from++; case 3: *to++ = *from++; case 2: *to++ = *from++; case 1: *to++ = *from++; } while (--n > 0); } }
Python でいう Generator 的なものを ANSI C の規格内で実装する。 Putty で実際に使われているらしい。
int evensThenOdds(int n) { int i; for (i = 0; i < n; i += 2) { yield i; } for (i = 1; i < n; i += 2) { yield i; } }
これを
int evensThenOdds(int n) { static int state = 0; static int i; switch (state) { case 0: for (i = 0; i < n; i += 2) { state = 1; return i; case 1:; } for (i = 1; i < n; i += 2) { state = 2; return i; case 2:; } } }
こう書く。 __LINE__
を使ってマクロを定義すれば、 yield
構文に近い感じで使える (ただし変数の取り扱いには注意が必要。 C++ なら functor にして、ローカル変数を全てメンバ変数に置きかえるのが良いとおもう) 。
Open and Efficient Type Switch for C++ (Y. Solodkyy, G. D. Reis, B. Stroustrup)
if (P1(x)) { statement1; } else if (P2(x)) { statement2; } else ... if (Pn(x)) { statementn; }
こういうのを、 memoization で速くするのに使う。
static unordered_map<decltype(T), size_t> jump_targets; size_t& jump_to = jump_targets[x]; switch (jump_to) { default: if (P1(x)) { jump_to = 1; case 1: statement1; } else if (P2(x)) { jump_to = 2; case 2: statement2; } else ... if (Pn(x)) { jump_to = n; case n: statementn; } }
ここだけ抜き出すと、別に普通の書き方もできるんじゃ、と思ってしまうのだけれど、関数型言語のパターンマッチのようなものを C++ のライブラリとして実装するのが目的で、マクロを実装するのに都合がいい(ようだ)。
© Yasuhiro Fujii <y-fujii at mimosa-pudica.net>, under CC-BY.