例外処理

このエントリーをはてなブックマークに追加
385デフォルトの名無しさん:2006/12/19(火) 06:47:46
C++でアプリを正常終了する時に例外を投げるのって邪道?
386デフォルトの名無しさん:2006/12/19(火) 07:57:38
>>385
普通に間違い。
387デフォルトの名無しさん:2006/12/19(火) 11:01:33
ずっと稼動してる様なアプリだと、シグナルがきたら終わるとかよくやるけど
そこで例外投げるってのはどうかと思う。

>例外は、それを取り巻くコードの
>構造と意味を、根底から覆します。例外は、プログラムの実行時
>セマンティックを一時的に繋ぎ変え、通常実行しているコードを迂回し、
>こういう状況でなければ決して実行されないコードを動作させます。
388デフォルトの名無しさん:2006/12/19(火) 11:45:38
間違いってことはないが、明らかに設計が普通ではない
389デフォルトの名無しさん:2006/12/19(火) 13:05:36
例外って飛んできたらexitするしかないような処理でしょ。
390デフォルトの名無しさん:2006/12/19(火) 15:42:32
暗号化したコードを復元するためのシグナルに、例外を使ったことがある。
反省はしない。
391デフォルトの名無しさん:2006/12/19(火) 18:07:19
どこから飛んできたのか探すのが大変だから、正常系の処理に例外は使うなってこった。遅いし。
明らかに復帰不可能な例外なら投げてもいいけど。
392デフォルトの名無しさん:2006/12/20(水) 01:05:59
>>389
それが決まってるなら exit() すればいいわけで、例外投げる意味が無い。
393デフォルトの名無しさん:2006/12/20(水) 01:50:27
>>392
exit()するとオブジェクトの始末がされないのでは?
394デフォルトの名無しさん:2006/12/20(水) 10:54:02
exitするしかないような事態で、正常に後始末できるの?
395デフォルトの名無しさん:2006/12/20(水) 11:05:25
>>392
ローカルオブジェクトのデストラクタは呼び出されないね。
意味が無いわけじゃないな。

だからといって例外の使い道がそんな場合に限定されるわけじゃないよ。
catch した人が処理を選択できるのが例外の重要な意義なんだから。

>>394
なんでできないと思うの?
396デフォルトの名無しさん:2006/12/20(水) 11:22:56
exitするしかないような事態って、ディスクとかネットワークトラブルとか思い浮かべてたんだけど
そんな状態で正常に後始末できるのかなと
397デフォルトの名無しさん:2006/12/20(水) 13:44:53
コード上のリソース(ヒープとかファイルハンドルとか)の後始末じゃね?
C++ だと例外安全かどうか気をつかうことになりそうだけどサ
398デフォルトの名無しさん:2006/12/20(水) 21:20:00
C++に正解などない。
書きたいように書けばいいじゃないか。
399デフォルトの名無しさん:2006/12/20(水) 21:24:31
しかし定石はあると言えないか?
400デフォルトの名無しさん:2006/12/21(木) 20:21:33
Hoge::Hoge():pint( new int ){};
これは間違いだと言いたい。
401デフォルトの名無しさん:2006/12/21(木) 21:30:16
>>400
何を根拠にそんなことが言える?
402デフォルトの名無しさん:2006/12/22(金) 15:05:04
Hoge::Hoge():pint( new int ){};
には別に悪いことはないんじゃない。

ただ、コンストラクタ/デストラクタで例外出すのはやめて欲しいがな
403デフォルトの名無しさん:2006/12/22(金) 15:09:36
>>402
コンストラクタは別にいいだろ。他にどうやって失敗を通知するんだ?
404デフォルトの名無しさん:2006/12/22(金) 15:39:18
コンストラト処理中に例外発生した半端なオブジェクトに対して デストラクタは呼ばれない
該当半端オブジェクトの、例外発生前までのうまくいっている部位の後始末をどうするかが面倒

ってことで、問題を簡単化するのに >>402 な希望だと思う
405デフォルトの名無しさん:2006/12/22(金) 15:57:05
それぞれのメンバが適切に RAII のルールを守っていれば話はもっと単純にできる。
それをせずに「コンストラクタから例外禁止」なんて新しいルールを作るのは良くない。
406デフォルトの名無しさん:2006/12/22(金) 15:58:24
>例外発生前までのうまくいっている部位の後始末をどうするかが面倒

後始末が必要なものはクラスで包めばいいじゃん。
407デフォルトの名無しさん:2006/12/22(金) 16:08:59
結局RAIIに徹しろっていう普通の関数の例外安全性の議論と大差ないよね>コンストラクタ
RAIIが面倒な場合は初期化子リストにtry-catch仕掛けることもできるし

一般にコンストラクタは強い例外安全性を達成するべきという主張なら十分納得できるんだけれど
408デフォルトの名無しさん:2006/12/22(金) 19:01:16
class A {
Hoge *hoge; //どっちともコンストラクタで例外を出す可能性があるクラス
Hage *hage;

A()
 try:hoge( new Hoge ), hage( new Hage ) { }
 catch( ... )
{
初期化子にtry-catch掛けられるったって、こういう状況は非常に面倒くさい。
new Hageで例外でたらどーすんだ?
}
409デフォルトの名無しさん:2006/12/22(金) 19:23:23
> 後始末が必要なものはクラスで包めばいいじゃん。
410デフォルトの名無しさん:2006/12/22(金) 19:30:10
> 後始末が必要なものはクラスで包めばいいじゃん。
初期化子を使わずにコントロールしろということなのかな?

>>408 の例だと
A()
{
 try { hoge = new Hoge; } catch(...) { throw ; }
 try { hage = new Hage; } catch(...) { delete hoge; throw ; }
};
411デフォルトの名無しさん:2006/12/22(金) 19:31:25
スマートポインタ使えばいいのに
412デフォルトの名無しさん:2006/12/22(金) 19:36:13
class A
{
std::auto_ptr<Hoge> hoge;
std::auto_ptr<Hage> hage;
A() : hoge(new Hoge), hage(new Hage) {}
};

これでどちらで例外が投げられても大丈夫。
413デフォルトの名無しさん:2006/12/22(金) 19:39:46
>>412
AのA のデストラクタが呼ばれないのは分かるけど、A::hoge と A::hage のデストラクタは呼ばれるの?
スマートポインタって自身のデストラクタ呼ばれないと機能しないような気がするのだが
414デフォルトの名無しさん:2006/12/22(金) 19:49:51
>>413
コンストラクタが完了したものはデストラクタが呼ばれる。
new Hageで例外が出ても、hogeは構築済みだからデストラクタが呼ばれる。
415413:2006/12/22(金) 19:57:38
>>414
てんきゅう。 自分もコード動かして確認した…
例外周りのメンバの取り扱いはややこしいねぇ

実際投げてる例外はおいといてくれw
class Hoge {
public:
 Hoge() { std::cout << "Construct Hoge" << std::endl; }
 ~Hoge() { std::cout << "Destruct Hoge" << std::endl; }
};

class Hage {
public:
 Hage() { int e=1; throw e; }
};

class A {
 std::auto_ptr<Hoge> hoge;
 std::auto_ptr<Hage> hage;
public:
 A() : hoge(new Hoge), hage(new Hage) {}
};

int main() { try { A a; } catch (...) { } return 0; }
416デフォルトの名無しさん:2006/12/23(土) 04:53:54
構築に成功したオブジェクトは必ず解体に成功する
構築に失敗したオブジェクトは解体する必要がない
何か気をつける余地が0になるのがRAIIのありがたいところ
417デフォルトの名無しさん:2006/12/23(土) 12:37:30
new Hageで確保されたメモリは開放されんの?>auto_ptr
418デフォルトの名無しさん:2006/12/23(土) 13:45:43
>>417
それはnew演算子の仕事だよ
419デフォルトの名無しさん:2006/12/23(土) 14:45:27
>>418
んーごめん。まったくわからん(´д`;
420デフォルトの名無しさん:2006/12/23(土) 14:59:49
>>419
new Hage の動作を分解すると、

void* storage = operator new (sizeof(Hage));
try { new (storage) Hage; }
catch (...) { operator delete (storage); throw; }

こんな感じになってるわけよ。
421デフォルトの名無しさん:2006/12/23(土) 16:33:22
ぉぉ。そうなんですか。
あざーす
422デフォルトの名無しさん:2007/01/15(月) 10:20:09
だれもいない
423デフォルトの名無しさん:2007/01/15(月) 11:05:00
じゃあ話題振るか。

システム内部は全てワイド文字で処理してるプログラムの例外って
みんなどうしてる?
std::exceptionのwhat()ってconst char*しか扱えないじゃない?
そうするとシステム内部のワイド文字列を例外に載せようとした時、
2つの解決方法があると思うんだ。

1つはその文字列をマルチバイト文字列にコンバートして
std::exception(の派生クラス)で投げる。
もう1つはワイド文字列を運べるwexceptionクラスを自作する。

1だと投げる時WCS>MBS、捕まえて表示するときMBS>WCSと
無駄な処理が発生する場合がある。
2だとSTLとかが投げる例外と自分のシステムが投げる例外が
同じ処理をする場合でも、いちいちcatch節を2つ書かなきゃならない。

まあ例外は全てMBSで扱って表示もMBSでしかしないってのもアリだけど
424デフォルトの名無しさん:2007/01/15(月) 11:44:20
typedef struct what {
int id,
wchar *hoge;
} what;

exception((char *)w);

425デフォルトの名無しさん:2007/01/15(月) 12:19:32
what()はデバッグ時にトレース出力くらいしか使ってないや。
426デフォルトの名無しさん:2007/01/15(月) 13:06:57
>>423
一番堅いけれど面倒なやり方として,
エラーの識別子のみを std::exception に載せる std::string の情報とする扱い方.
エラーを人間に読めるようなメッセージに変換する必要があるならば,
メッセージカタログなりを使ってエラーの識別子とエラーメッセージとの対応を取る.
この場合,表示するメッセージを MBS とするか WCS とするかのポリシーは
メッセージカタログの責任範疇となって, std::exception と独立となるのが利点.
427デフォルトの名無しさん:2007/01/15(月) 20:08:02
>>426
std::exceptionにstd::stringに埋めちゃうのは、std::stringのコピーコンストラクタ
が例外を発射する可能性があるから、やばいのでは。

例外処理の運用については、>>423的な悩みも含めて Boost の
http://www.boost.org/more/error_handling.html がとてもよくまとまっていると思う。
(翻訳版はバージョンが古いのか、内容が違う)

あとはexception safty(例外安全性(?))やexception neutral (例外中立性(?))
なクラスの設計、実装について、Exceptional C++, More Exceptional C++,
Exceptional C++ Style の該当項目に目を通せばOK。
428426:2007/01/15(月) 22:01:37
>>427
>std::exceptionにstd::stringに埋めちゃうのは、std::stringのコピーコンストラクタ
>が例外を発射する可能性があるから、やばいのでは。
この問題は完全に見落としていました. newsgroup などを漁った感じでは
std::exception の設計・実装の一部にも問題があるといえる状況のように感じます.
http://tinyurl.com/y7c2wu

http://www.boost.org/more/error_handling.html
自分が426で書いたことは "How should I design my exception classes?" の
5に書いてあることにおおよそ対応しますね.
というか,そもそも426で書いた内容は Peter Dimov 氏の受け売りでしかないですけれど.
http://tinyurl.com/umvgh
429423:2007/01/15(月) 22:06:59
>>426
Win32APIのGetLastError()とFormatMessage()みたいなことかな。
できれば問題が起きた原因たるオブジェクト(の内容)を
例外ハンドリングしてるところまで持ってって表示してあげたいんだけど、
その方法じゃ無理だね。困った。

>>427
つまり↑みたいな野望は無駄な足掻きってことかな…。
staticなエラーメッセージ保存領域作っておいて
そこにコピーしてから例外飛ばすとか?



てか、もしかしたら例外で処理すべき問題じゃないのかもしれない…
430デフォルトの名無しさん:2007/01/16(火) 08:01:01
>>423
規格にある std::exception::what() についての記述には、
wstring に変換して表示するのに適した NTMBS でもよい、とある。

規格に沿うようにしたいのなら、このアプローチがいいってことね。
431デフォルトの名無しさん:2007/01/16(火) 11:47:14
>>429
>Win32APIのGetLastError()とFormatMessage()みたいなことかな。
みたいなことですね。

>できれば問題が起きた原因たるオブジェクト(の内容)を
>例外ハンドリングしてるところまで持ってって表示してあげたいんだけど、
エラーの文脈情報を付加したいという話ですよね。そのエラーに対応する
std::exceptionの派生クラスのメンバ変数に持たせれば可能なのでは。

>>>427
>つまり↑みたいな野望は無駄な足掻きってことかな…。
いや、427ではstd::exceptionにstd::stringを「埋める」とやばいという話を
しましたけど、ポインタで(例えばstd::auto_ptr<std::string>)で持たせれば
問題はありません。string以外の文脈情報についても同様です。
432デフォルトの名無しさん:2007/01/16(火) 17:03:34
今度はnewが例外を投げるのでは、という心配が。
433デフォルトの名無しさん:2007/01/16(火) 18:27:19
>>432
例外クラスの設計で注意しなきゃいけないのは、スタックの巻き戻しを
している最中に呼ばれる例外クラスのコピーコンストラクタが例外を
発射しないことです。
この状況になると通常、terminate()直行で、どこのエラーで落ちたのか
もわからない結果になります。

それ以外の状況なら例外処理中に例外が発生しても対処は可能でしょう。
434デフォルトの名無しさん
.NET
信頼性に関するベスト プラクティス
http://msdn2.microsoft.com/ja-jp/library/ms228970(VS.80).aspx