Namaran

Code daily. Without assist.

2026-08-23

calc_dispatch.c

typedef int (*op_fn)(int);

int square(int n) { return n * n; }
int cube(int n) { return n * n * n; }

typedef struct {
    op_fn table[2];
    int cache[16];
    unsigned char cached[16];
} calc_t;

int calc_run(calc_t *c, int op, int n) {
    if (!c->cached[n]) {
        c->cache[n] = c->table[op](n);
        c->cached[n] = 1;
    }
    return c->cache[n];
}

int main(void) {
    calc_t c = { .table = { square, cube } };
    printf("%d\n", calc_run(&c, 0, 3));
    printf("%d\n", calc_run(&c, 1, 3));
}

c->table[op]squarecubeかを実行時に選んでいます(動的ディスパッチ)。1行目は9で正しいのですが、cube(3)27を出すはずの2行目も9になってしまいます。何が問題で、どう直せばよいでしょうか?

Answer

cachecachednだけをキーにしていて、どのopで計算したかを覚えていません。calc_run(&c, 0, 3)square(3)を計算するとcached[3]が立ってしまい、続くcalc_run(&c, 1, 3)は同じn=3を見て「もう計算済み」と判断し、cubeを一度も呼ばずにさっきのsquareの結果を返してしまいます。動的ディスパッチでは呼ばれる関数が実行時に変わるので、メモ化のキャッシュも「引数」だけでなく「どの関数を呼んだか」まで区別しないと、別の関数の結果を使い回してしまいます。opごとに別のキャッシュを持たせれば直ります:

typedef struct {
    op_fn table[2];
    int cache[2][16];
    unsigned char cached[2][16];
} calc_t;

int calc_run(calc_t *c, int op, int n) {
    if (!c->cached[op][n]) {
        c->cache[op][n] = c->table[op](n);
        c->cached[op][n] = 1;
    }
    return c->cache[op][n];
}