例外処理とその影響

Home > 2005-07 / より良いC > This Entry [com : 0][Tb : 0]

2005-07-28

 今回は、例外処理について、かなり突っ込んだお話をしたいと思います。本題に入る前に、まずは例外処理がどんなのものであるか、今一度確認しておきます。

#include <stdio.h>

class X {};

void foo()
{
  X x;
  throw x;
}

int main()
{
  try
  {
    foo();
    puts("A");
  }
  catch (X& e)
  {
    puts("B");
  }
  return 0;
}


といったサンプルプログラムを実行すると、"A"ではなく、"B"が出力されます。

これは、fooの中でxを例外として送出しているからで、関数呼び出しツリーのどんなに深いところからでも、例外が発生すれば一気に元のところまで制御が移ります。この点においては、setjmp/longjmpと非常に良く似ています。

例外がsetjmp/longjmpと最も違うのは、非局所分岐によってスタックを巻き戻す際、途中にあった自動記憶域期間のオブジェクトのデストラクタを順に呼び出すことができる点です。このようにすることで、途中にあったオブジェクトが解体されることなく彷徨うことを防げます。

例外処理機構を持つ言語は他にもたくさんありますが、それらの多くは、try/catchの他にfinally節があり、そこでリソースの解放等を行うようにしています。しかし、そのような方法では、解放すべき対象が複数あると、try/catch/finallyを入れ子にするしかありません。

C++では、例外処理とデストラクタという、非常に相性の良い読み合わせによって、非常にすっきりした記述ができるようになっています。



 C++が例外処理とデストラクタを持っていることは大きな強みですが、それは同時に大きな代償を払うことにもつながっています。つまり、例外処理機構を実現するために、ランタイムだけでなく、通常のコードまでをも肥大化させることになるのです。

コンパイラは、スタックの巻き戻し時に、どんなデストラクタを呼び出すべきかの情報をブロックごとに生成しなければなりません。また、通常はデストラクタを呼び出すためのコードも生成する必要があります。これは、下手をすれば関数本体よりも大きなサイズになることがあります。

このように、例外処理はプログラムサイズを大幅に肥大化させます。PC等のプログラムであればプログラムサイズはそれほどシビアではないかもしれませんが、組み込み環境では致命傷にさえなりかねません。これが例外処理をオフにすべき第一の理由です。

 例外処理をオフにすべき第二の理由は、例外安全に関わる問題です。例外安全はかなり難度の高い話題でもあるので、詳細は別の機会に譲るとして、ごく大雑把にいえば、例外が発生したことでリソースリークやその他の挽回不能な事態に陥らない設計が必要というです。

例えば次のコードを見てください。

void foo()
{
  X* p = new X;
  hoge(p);
  delete p;
}


非常に単純なコードですが、このコードは例外に対して安全ではありません。なぜなら、もしhogeが例外を送出すれば、次の行のdelete p;は実行の機会を失い、pが指すインスタンスはリークするからです。

C++では、スカラ型の操作や明示的に例外が送出されないことを指定された関数以外は、すべて例外が送出される可能性があります。仮に関数の実装を紐解いて、throwがないことを確認しても、将来にわたって保証されるわけではありません。演算子までもがそうなのです。

Cであっても、返却値等でエラーコードを返す場合、返却値型がvoidでない限り、すべての関数の返却値をチェックしなければなりませんが、C++の例外も結局は同じで、チェックコードは書かなくても構いませんが、配慮は必要だということです。

そして、返却値で返されたエラーコードを無視してもコンパイラは何もいわないのと同様、例外安全でないコードを書いてもコンパイラは何も教えてくれません。例外安全への対処は、ある程度は体で覚えなければならないことなのです。

このように例外安全への配慮というのは、高い技術力が要求されます。これについてはビジネスアプリケーション等でも同じですが、組み込みではより高い信頼性が要求されるため、十分な注意が必要となります。

 例外処理をオフにすべき理由はもうひとつあります。それは、組み込み用の汎用のコンパイラは、例外処理に関してマルチタスク環境に対応していないということです。OSなしの環境であれば問題ありませんがμITRON等を使う場合は大問題です。

これはTOPPERS/JSPカーネルのように、例外処理を含めてC++に対応したOSを使うか、コンパイラを改造するか、C++を使うタスクを1つに制限するしか対策がありません。

 こんな風に書くと、例外は怖いので決して使うべきではないと短絡的な発想になってしまうかもしれません。しかし、思い出してみてください。多くの方は、Cを覚えたての頃にポインタの危険性に冷や冷やしながらも、いったん使いこなせればこんな強力な武器はないことに気づいたはずです。

例外処理も、そう意味ではポインタと同じようなもので、しっかりと理解し、使いこなせるようになるまで体で覚えれば、これほど強力な武器はありません。

Comment

Post a Comment









管理者にだけ表示を許可

Trackback

http://cppemb.blog17.fc2.com/tb.php/11-213f74b9

C++と組み込み環境 | Page Top▲

New >>
Embedded C++について
<< old
より良いCとしてのC++
ブログ内検索
RSSフィード