未熟にもしてやられた

よくマクロでdo{ ... }while(0);という書き方がされている。単にスコープを作るだけなら{ ... }のほうが見やすいと思う。しかし、do{ ... }while(0);{ ... }は違う。

何が違うって、do{ ... }while(0);はあくまでもループなので、continue;break;の抜け先が異なるのだ。

展開された状態で

  for(i=0; i<100; i++){
    do{
      printf("Hello, World %d\n", i);
      if(i==10) break;
    }while(0);
  }

は100回ループを回るが、

  for(i=0; i<100; i++){
    {
      printf("Hello, World %d\n", i);
      if(i==10) break;
    }
  }

は10までしか表示されない。どちらが望ましいかはケースによって異なる。この違いを認識しないでいろいろなマクロを書いていると、わけがわからないバグとしていつか表面に現れるだろう…っていうか、実際に(もうちょっと複雑なケースで)バグったから書いてるんだけど(笑)。

実際にバグったのを説明すると、リストをたどる用のマクロ(foreach〜forendで囲むタイプのもの)というのを使ってて、do{ ... }while(0);じゃなくて{ ... }を使っていた。で、次の要素に移るとか、forendのほうでもちょっと処理していた。で、ループの中でcontinueを書いてもcontinueになってなかったのだ。forend側の}の前に書いてあった、いわゆるi=i->next;の処理が実行されない。do{ ... }while(0); i=i->next;のようにcontinueで飛びたい部分を囲むとよい。ただこれじゃ、breakで抜けられなくなっちゃうのだけどね。要はcontinueとbreakのどちらを取るかって話だが、for文を使ってi=i->nextな部分をforに押し込めるのが正しいと思う。あるいはbreakとcontinueだとbreakのほうが使うから、continueを禁止するか。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です