switch-case の特殊な使い方

C/C++ の switch - case には、必ずしも caseswitch 直下に置く必要がないという、他の言語にはあまり継承されていない特徴がある。この用法をなんとなく集めてみた。

Duff's device

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);
    }
}

Generator

Coroutines in C (S. Tatham)

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 にして、ローカル変数を全てメンバ変数に置きかえるのが良いとおもう) 。

Memoization Device

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.