NOEMBLEM/エンブレムが設定されていません。

メールの詳細(トピック表示)

C++のエラー処理をエラーコード主体で書く場合のstd::bad_allocなどの扱いについて

投稿者:wataさん  2009/07/29 22:37  MLNo.13594   [メール表示]

こんにちは。渡邉です。

みなさんはC++でのエラー処理をどのように書いていますか?

おそらく次の3つのどれかだと思うのですが、

1. エラーコードのみを使う

2. 例外のみを使う

3. エラーコードと例外を使い分ける

1.とした場合、newが投げるstd::bad_allocだとか、
std::vectorなどのコンテナが投げる例外などへの処理はどうしているのでしょうか?
コンパイラオプションで例外機構を無効にして、std::vectorなどを使わないように
コーディングしているのでしょうか?

というのは、最近以下のサイトで例外のメリットとデメリットを
簡潔にまとめた良記事を見つけまして、実際のところみなさんは
どうしているか知りたいと思いまして。
特に例外のデメリットを回避するために、例外を排除したC++のコーディングは
どんな感じになるのか知りたいです。

C++ Exceptions: Pros and Cons
http://www.codeproject.com/KB/cpp/cppexceptionsproetcontra.aspx

例外のメリット:

1. 例外はエラー処理を正常処理フローから分離することで可読性、堅牢性、拡張性を高める
2. 例外を投げることはコンストラクタからエラーを報告する唯一の方法である
3. 例外はエラーコードと違い無視するのが難しい
4. 例外は深いネストから伝搬するのが簡単
5. 例外にはエラーコードよりもたくさんの情報を含んだユーザ定義型を利用できる
6. 例外オブジェクトと例外ハンドラとのマッチングは型システムによって行われる

例外のデメリット:

1. 例外は目に見えない関数の出口を作ってしまうためコードを読んで検査することが難しくなる
2. 例外はリソースリークを引き起こしやすい。特にビルトインのガーベッジコレクタとfinalyブロックがない言語で
3. 例外安全なコードを書くのは難しい
4. 例外は高価であり、必要最小限のコストに抑えるという原則に反している
5. 例外はレガシーコードに組み込むのが難しい
6. 例外は正常処理をこなすための道具として乱用されやすい


Gg[ubN}[N


  • MLNo.13595   "Kazutoshi Satoda"さん  (0) 2009/07/30 06:36  [メール表示する]
    hwatanabe.japan@… wrote:
    > みなさんはC++でのエラー処理をどのように書いていますか?
    >
    > おそらく次の3つのどれかだと思うのですが、
    >
    > 1. エラーコードのみを使う
    >
    > 2. 例外のみを使う
    >
    > 3. エラーコードと例外を使い分ける

    願わくば 2 でいきたいんですが、エラーコードを使う既存のコードや別の
    コードベースとの組み合わせのため実際には 3 になってたりします。

    コンパイラやそのほか開発環境の事情によって 1 にせざるをえないことも
    あります。

    > 1.とした場合、newが投げるstd::bad_allocだとか、
    > std::vectorなどのコンテナが投げる例外などへの処理はどうしているのでしょうか?
    > コンパイラオプションで例外機構を無効にして、std::vectorなどを使わないように
    > コーディングしているのでしょうか?

    スタンドアロンの組み込みソフトであれば、メモリ不足に陥る状況をソフト側の
    調整で事前に回避(確保方法やタイミングを見直したり、そもそものヒープの
    割り当てサイズを調整したり)できるので、メモリ不足時には即ブレーク
    (あるいはabort() )するように ::operator new をユーザー定義してやり、
    テスト(デバッグ)段階でバグとしてすべて潰してしまう、という選択肢が
    考えられます。

    std::allocator は ::operator new を使って実装されるものと標準で
    定められているので、これによって std::vector 内部のメモリ確保でも同様の
    動作になります。


    以下は件名に挙げられた話題からは外れるのですが・・・

    > というのは、最近以下のサイトで例外のメリットとデメリットを
    > 簡潔にまとめた良記事を見つけまして、実際のところみなさんは
    > どうしているか知りたいと思いまして。
    > 特に例外のデメリットを回避するために、例外を排除したC++のコーディングは
    > どんな感じになるのか知りたいです。
    >
    > C++ Exceptions: Pros and Cons
    > http://www.codeproject.com/KB/cpp/cppexceptionsproetcontra.aspx

    挙げられたデメリットのうち、いくつかは「例外を使う」という選択のうえでも
    問題にならない(ことも多い)ものと思いますので、これらを回避するためと
    いうだけで「例外を排除」と標準から外れる大きな選択が必要になるのか、
    疑問に感じました。

    記事を詳しく読むと、箇条書きの項目それぞれについてよくある反論を挙げて、
    それを踏まえた説明がされているのですが、見出しにはちょっと例外を使わない
    ほうへのバイアスがかかってしまっているように思います。

    というわけで、いくつかについて「例外を使う」という立場で簡単に反論を
    挙げてみます。

    > 1. 例外は目に見えない関数の出口を作ってしまうためコードを読んで検査することが難しくなる

    どこで例外が飛んでもいいように RAII を徹底していれば、あまり問題になる
    ことが無いと思います。

    これが問題になるような関数は、そもそも関数分けなど他の点に根本的な問題が
    あることも多いと思います。

    > 2. 例外はリソースリークを引き起こしやすい。特にビルトインのガーベッジコレクタとfinalyブロックがない言語で

    これも RAII を徹底していれば、あまり問題にならないはずです。

    記事中では、わざわざ不要な new/delete を書いてリークを発生させるような
    ことは避けられない、というかなり苦しい説明がされています。

    それについても、「delete を書かない」という基準でコードを見ていくことで
    対抗できます。単一の delete があれば std::auto_ptr などの
    スマートポインタを使って排除、配列 delete があれば std::vector を使って
    排除、という感じです。

    > 3. 例外安全なコードを書くのは難しい

    例外安全にまつわる話は C++ 例外に特有のものであるかのように語られる
    ことが多いのですが、実はエラーコードを使っても同じ話になります。

    記事中の説明では「それでも・・・」という感じで例外を使用する場合の数々の
    約束事(デストラクタから例外を投げない、 catch では参照で受け取る、
    例外仕様について、・・・)を学ぶコストを挙げていますが、その点は例外安全
    とは関係ありません。

    ただ、別の話として上記のような約束事を学ぶコストは確かにあると思います。

    > 4. 例外は高価であり、必要最小限のコストに抑えるという原則に反している

    記事中でも挙げられていますが、正常フローの効率をほとんど下げない(むしろ
    エラーコードを使う場合より効率化することもある)コンパイラもあり、
    そういった実装が増える(生き残る)傾向にあります。

    例外によるオーバーヘッドが発生することも確かにありますが、それが
    ボトルネックになっているのであればそこだけエラーコードに置き換えるという
    こともできます。

    --
    k_satoda

  • MLNo.13596   つぼさん  (0) 2009/07/30 14:16  [メール表示する]
    自分も2.もしくは3.で行きたいです。

    バイナリ境界ではエラーコードを使いますが、基本的には
    例外使いたい、使いましょう、よりです。

    ですが、理想論と現実解は常にギャップがあるもので、
    結局エラーコードにのみに妥協しています。

    というのも、メリット3.で述べられている伝播についていつも
    悩まされているからです。

    たとえば、実装詳細コードで投げる例外を、ずっと上のアプリケーションロジックで
    キャッチする場合、どんなエラーが起こりえるか、プログラマが知らなくてはいけません。
    プログラマだけではなく、プログラムコードもヘッダファイルのインクルードで
    共通語(例外クラス)を知らなくては細かい状況に対応した復帰作業ができません。

    std::runtime_errorの文字列だけで済む場合はいいですが、ファイルオープンにしても
    ファイルが存在していないのか、転送中のネットワークエラーなのかによって
    アプリケーションロジックを分岐したいこともあります。

    したがって、実装の詳細が伺える共通ヘッダを用意するか、抽象化レベル毎に
    例外オブジェクトも少しずつ抽象化しながら、再スローするといった解決法しか
    思いつきません。

    自分の体験談になりますが、文字列とあるオブジェクトを対で保存するクラスを
    作ったとき、std::mapで実装するにしろ、std::vectorで実装するにしろ
    結局、キー重複例外やキー存在しない例外や最大保持数超過例外など
    実装クラスが投げる例外(mapは投げないですが)を適切に抽象化しないと、
    上層部で処理するコードが実装べったりになってしまうことになったんです。

    で、ほぼ関数レベルで例外を変換しなきゃいけないんだったらエラーコードでも
    同じかと思った次第です。実際は、エラーコードと文字列を運べるオブジェクトを
    渡してましたが。

    気持ちは例外大好き、でもエラーコードオブジェクトに妥協という感じです。
    どちらでも同じなら例外しろと言われそうですが、そのときはプラグインも
    絡んでいたのでエラーコードにしました。使ってみるとエラーコードオブジェクトも
    そんなに悪くないと思います。

    ほかの皆さんはいかがですか?自分もこの辺はかなり興味深いトピックだと
    思うので、いろいろな意見が聞きたいです。

    2009/07/29 6:37 に <hwatanabe.japan@…> さんは書きました:
    > こんにちは。渡邉です。
    >
    > みなさんはC++でのエラー処理をどのように書いていますか?
    >
    > おそらく次の3つのどれかだと思うのですが、
    >
    > 1. エラーコードのみを使う
    >
    > 2. 例外のみを使う
    >
    > 3. エラーコードと例外を使い分ける
    >
    > 1.とした場合、newが投げるstd::bad_allocだとか、
    > std::vectorなどのコンテナが投げる例外などへの処理はどうしているのでしょうか?
    > コンパイラオプションで例外機構を無効にして、std::vectorなどを使わないように
    > コーディングしているのでしょうか?
    >
    > というのは、最近以下のサイトで例外のメリットとデメリットを
    > 簡潔にまとめた良記事を見つけまして、実際のところみなさんは
    > どうしているか知りたいと思いまして。
    > 特に例外のデメリットを回避するために、例外を排除したC++のコーディングは
    > どんな感じになるのか知りたいです。
    >
    > C++ Exceptions: Pros and Cons
    > http://www.codeproject.com/KB/cpp/cppexceptionsproetcontra.aspx
    >
    > 例外のメリット:
    >
    > 1. 例外はエラー処理を正常処理フローから分離することで可読性、堅牢性、拡張性を高める
    > 2. 例外を投げることはコンストラクタからエラーを報告する唯一の方法である
    > 3. 例外はエラーコードと違い無視するのが難しい
    > 4. 例外は深いネストから伝搬するのが簡単
    > 5. 例外にはエラーコードよりもたくさんの情報を含んだユーザ定義型を利用できる
    > 6. 例外オブジェクトと例外ハンドラとのマッチングは型システムによって行われる
    >
    > 例外のデメリット:
    >
    > 1. 例外は目に見えない関数の出口を作ってしまうためコードを読んで検査することが難しくなる
    > 2. 例外はリソースリークを引き起こしやすい。特にビルトインのガーベッジコレクタとfinalyブロックがない言語で
    > 3. 例外安全なコードを書くのは難しい
    > 4. 例外は高価であり、必要最小限のコストに抑えるという原則に反している
    > 5. 例外はレガシーコードに組み込むのが難しい
    > 6. 例外は正常処理をこなすための道具として乱用されやすい
    >
    >
    > 【MLコミュホームページ】http://www.freeml.com/cppll
    >
    > --[PR]------------------------------------------------------------------
    > ◇◆◇◆ 憧れの4LDKや共用施設充実マンション    ◇◆◇◆
    > ◆◇◆◇賃貸じゃ難しい?理想の住まい探しは早めの資料請求で先手!◆◇◆◇
    > ◇◆◇◆ これから販売予定のおNewなマンション、即チェック ◇◆◇◆
    > http://ad.freeml.com/cgi-bin/sa.cgi?id=e0tjr
    > ------------------------------------------------------------------[PR]--
    > ■GMO INTERNET GROUP■ GMO INTERNET www.gmo.jp
    >
    >

  • MLNo.13597   nyankitchさん  (0) 2009/07/30 18:14  [メール表示する]
    ニャン吉です。

    自分は std::exception を派生させてエラーコードを持たせてます。
    で、エラーコードから what_ を作ってます。
    でも、std::exception::what() が throw() なのに対して
    std::string::c_str() は throw() では無いのでスッキリしないです。

    class error_exception : public std::exception {
    public:
    explicit error_exception(int error_code);
    virtual ~error_exception() throw();

    virtual const char* what() const throw();
    int error_code() const throw();

    private:
    int error_code_;
    std::string what_;
    };

    あと、基底クラスも std::logic_error か std::runtime_error のほうが
    適している気もして、このへんの使い分けが良く分からんです…


    --------------------------------------
    Power up the Internet with Yahoo! Toolbar.
    http://pr.mail.yahoo.co.jp/toolbar/

  • MLNo.13598   wataさん  (0) 2009/07/30 19:40  [メール表示する]
    To つぼさん

    渡邉です。

    >ですが、理想論と現実解は常にギャップがあるもので、
    >結局エラーコードにのみに妥協しています。

    この場合はSatodaさんが書かれているように、
    newに失敗したら即abortするような特別な対応をしているのでしょうか? それとも、newにnothowを指定するとか、STLを使わないとか。

    ルーズなC++プログラムでありがちなのは、自分の書いた関数は
    エラーコードベースで律儀にチェックしているけど、
    newとかSTLも普通に使っていて、それらが投げる例外には無防備というがあると思います。これが無知によるものか、見切った上での設計によるものなか判然とせず、もやもやすることが多いです。



  • MLNo.13599   つぼさん  (0) 2009/07/31 02:21  [メール表示する]
    オブジェクトをnewしたいけど、失敗したとき回復したければ
    即座にbad_allocを捕捉してエラーコードオブジェクトに
    状況を記録します。

    エラーコードは無視されるといいますが、戻り値ではなく
    出力パラメータとしてエラーコードオブジェクトを渡す仕様に
    すれば、少しは注意を促すことが
    できます。

    出力パラメータをポインタにしてデフォルトをnullにすれば
    オプションにできますし、参照にすれば強制できます。

    プログラムの続行が不可能な場合は結局abortさせるしかないでしょうし、
    何もしなくても勝手にabortしますし。

    すべての例外を監視するのは不可能だと思いますので、要所要所で
    捕捉していくしかないと思います。自分のプログラムがざるだと
    思われるのが嫌なら、バイナリ境界かmainで捕捉しておけば
    いいんじゃないでしょうか。

    あと根本を覆すようですが、
    「回復処理を考えているということ自体、起りえるエラーは例外ではない」
    という考え方もあります。自分がこういう考えかたをするように
    なってからは、外部要因のランタイムエラーは例外処理ではなく
    エラーコードを使うようになりました。普通のPCを相手にしているなら
    new失敗は例外として、最上部で捕捉、終了でいいんじゃないでしょうか。

    でも、このあたりは主観もあるので不毛な議論になりがちです。

    2009/07/30 3:41 に <hwatanabe.japan@…> さんは書きました:
    > To つぼさん
    >
    > 渡邉です。
    >
    >>ですが、理想論と現実解は常にギャップがあるもので、
    >>結局エラーコードにのみに妥協しています。
    >
    > この場合はSatodaさんが書かれているように、
    > newに失敗したら即abortするような特別な対応をしているのでしょうか? それとも、newにnothowを指定するとか、STLを使わないとか。
    >
    > ルーズなC++プログラムでありがちなのは、自分の書いた関数は
    > エラーコードベースで律儀にチェックしているけど、
    > newとかSTLも普通に使っていて、それらが投げる例外には無防備というがあると思います。これが無知によるものか、見切った上での設計によるものなか判然とせず、もやもやすることが多いです。
    >
    >
    >
    >
    > 【MLコミュホームページ】http://www.freeml.com/cppll
    >
    > --[PR]------------------------------------------------------------------
    > ◇◆◇◆ 憧れの4LDKや共用施設充実マンション    ◇◆◇◆
    > ◆◇◆◇賃貸じゃ難しい?理想の住まい探しは早めの資料請求で先手!◆◇◆◇
    > ◇◆◇◆ これから販売予定のおNewなマンション、即チェック ◇◆◇◆
    > http://ad.freeml.com/cgi-bin/sa.cgi?id=e02By
    > ------------------------------------------------------------------[PR]--
    > ■GMO INTERNET GROUP■ GMO INTERNET www.gmo.jp
    >
    >

  • MLNo.13601   道化師さん  (1) 2009/08/03 23:18  [メール表示する]
    道化師です。

    # エラーハンドリングと聞いて!

    以下、質問者の聞きたいことじゃなくて、自分の言いたいことを
    一方的に述べてるだけですが、ご参考までに。

    【[cppll:13594] C++のエラー処理をエラーコード主体で書く場合...】
    >みなさんはC++でのエラー処理をどのように書いていますか?

    今現在は概ねケースバイケースでやってますが、C++に限定せず、
    エラーハンドリングそのものの本来あるべき姿としては木構造
    エラーハンドリングをベースにするべきだと考えています。

    木構造エラーハンドリングの必要性を簡単に説明しておきますと
    そもそも関数呼び出しというものが木構造を形成し且つ全ての処理
    が問題発生時に後続の処理を放棄していいというわけではなく、
    問題発生時に後続の処理を実施中にさらに問題が起きる可能性もあ
    るわけで、そういった状況下では、エラーそのものが木構造を形成
    する為、木構造エラーハンドリングが自ずと必要になってきます。

    cf. エラーハンドリングフレームワーク
    http://ml.tietew.jp/cppll/cppll/article/13411
    http://d.hatena.ne.jp/wraith13/20080101/1199177152

    # C++0x で採用予定の nested_exception も木構造例外に対応しておらず
    # 本格的なエラーハンドリングに耐えうるものではないとしてコメント
    # をあげています。
    #
    # http://home.roadrunner.com/~hinnant/issue_review/lwg-active.html#1132
    # http://d.hatena.ne.jp/wraith13/20081231/1230715424 (日本語原文)


    【[cppll:13594] C++のエラー処理をエラーコード主体で書く場合...】
    >6. 例外は正常処理をこなすための道具として乱用されやすい

    パフォーマンス的な理由を除けば、そもそも例外を正常処理に使うべき
    でないとする論拠が薄く現時点においてそれを前提とするのは時期尚早
    だと考えています。

    cf. 超例外原理主義
    http://d.hatena.ne.jp/wraith13/20090624/1245798067
    http://d.hatena.ne.jp/wraith13/20090624/1245851228

    □■□■ Wraith the Trickster □■□■
    ■□■□ 〜I''ll go with heaven''s advantage and fool''s wisdom.〜 ■□■□
    [2009-08-30 開催の Future Language TV でスピーカーやります。]
    http://d.hatena.ne.jp/wraith13/20090730/1248958399

  • MLNo.13602   wataさん  (0) 2009/08/06 13:02  [メール表示する]
    To Tsubokawaさん

    渡邉です。返信が送れてすみません。

    >オブジェクトをnewしたいけど、失敗したとき回復したければ
    >即座にbad_allocを捕捉してエラーコードオブジェクトに
    >状況を記録します。

    なるほど。自分の書いたコードでは例外を使わないようにし、
    newとか例外が発生する機能を使う場合は、
    その場で例外を補足してエラーコードに変換すると。

    以下のサイトで例外に懐疑的なJoel Spolsky氏も同じ方法で
    やっているといっていますね。

    Exceptions:
    http://www.joelonsoftware.com/items/2003/10/13.html#

    People have asked why I don't like programming with exceptions.
    In both Java and C++, my policy is:

    1. Never throw an exception of my own
    2. Always catch any possible exception that might be thrown
    by a library I'm using on the same line as it is thrown and
    deal with it immediately.

    うーん悩ましい。

    私もRAIIや、ひとつの関数で一つの仕事の原則を心がければ、
    例外のほうがメリットが多いと思っているのですが。

    C++の現場ではやはりエラーコード派のほうが多いのでしょうか?

    他の言語(C#, Javaなど)ではどうなのでしょうか?
    他の言語では混乱なく例外を使っているのでしょうか?
    #cppll_noviceに移動したほうがよいかも


メールへの返信はMLのメンバーしかできません。

更新順メールリスト