Friday 10 August 2007

Switch 文の実装



Cのswitch文にポインタが使えないとかいう話があったので、うっかり実装を考えると言う羽目に。言うのは簡単なんだよな〜

まず、C でコンパイルしてみる。

int a,b,c,d;

int
main(int ac,char *av[])
{
  int *p[] = {&a,&b,&c,&d};
#if 0
  switch((int)(p[ac])) {
  case (int)&a:
     printf("a\n"); break;
  case (int)&b:
     printf("b\n"); break;
  case (int)&c:
     printf("c\n"); break;
  case (int)&d:
     printf("d\n"); break;
  default:
     printf("unknown\n");
  }
#endif
#if 1
  switch((p[ac])-&a) {
  case &a-&a:
     printf("a\n"); break;
  case &b-&a:
     printf("b\n"); break;
  case &c-&a:
     printf("c\n"); break;
  case &d-&a:
     printf("d\n"); break;
  default:
     printf("unknown\n");
  }
#else
  if (p[ac]==&a) {
     printf("a\n");
  } else if (p[ac]==&b) {
     printf("b\n");
  } else if (p[ac]==&c) {
     printf("c\n");
  } else if (p[ac]==&d) {
     printf("d\n");
  } else {
     printf("unknown\n");
  }
#endif

確かに動かない。
xxxx.c:27: error: case label does not reduce to an integer constant

if 文なら動く。switch 文はtable参照にコンパイルするための構文なので、そのtableを作るためにはコンパイル時に定数である必要がある。アドレスは定数じゃないので、どうしても実行時に解決しないとだめ。

実行時に解決されるってことは、table にしても、結局、hashかなんかで引く必要があるってことだよ。sort も出来ないので、binary search も出来ないし。

結局、考え付くのは、switch 文があったら、if/else if に書き換えてコンパイルするって手法だな。ポインタをswitch文に使える言語では、おそらくそうなっていると思った方が良い。switch 文だから速いとは限らないって奴だね。

hash or sort でtableを実行時に再構成するなら、 まぁ、やってやれないことはないんだが、いつ誰がやるかっていう問題があるので、 それはコンパイラではなく自分でやるべきだと思う。

そもそも、ポインタ持ってるんだったら、state pattern 使って、

  void exec_a() { printf("a\n"); }
  void exec_b() { printf("b\n"); }
  void exec_c() { printf("c\n"); }
  void exec_d() { printf("d\n"); }

  struct sw {
    void (*exec)();
  } a = { .exec = exec_a} ,b = { .exec = exec_b} ,
   c = { .exec = exec_c} ,d = { .exec = exec_d} ;

  int
  main(int ac,char *av[])
  {
    struct sw *p[] = {&a,&b,&c,&d};
    p[ac]->exec();
    return 0;

  }

とかしろよ。これだから、××な奴は... と思ったのだった。結局、言語仕様が複雑になるだけじゃん。Perl がswitch文を持たない理由の一つなんだろうな〜

No comments: