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.