問題:
長い間、私はexceptions
メカニズムが本当に何をすべきかを実際には解決しないと感じているため、メカニズムについて心配しています。
クレーム:このトピックについては長い議論があり、それらのほとんどはexceptions
エラーコードの比較と返送に苦労しています。これは間違いなくここのトピックではありません。
エラーを定義しようとすると、Bjarne Stroustrup&Herb SutterのCppCoreGuidelinesに同意します
エラーは、関数が公示された目的を達成できないことを意味します
クレーム:exception
メカニズムは、エラーを処理するための言語セマンティックです。
クレーム:私には、タスクを達成しないための機能には「言い訳」がありません:機能が結果を保証できないように事前/事後条件を誤って定義したか、開発に時間を費やすために特定の例外的なケースが十分に重要ではないと考えられます解決策。IMOでは、通常のコードとエラーコードの処理の違いは(実装前に)非常に主観的なものだと考えています。
クレーム:例外を使用して、事前または事後条件が維持されていないことを示すことはexception
、主にデバッグの目的で、メカニズムの別の目的です。私はexceptions
ここのこの使用法をターゲットにしません。
多くの書籍、チュートリアル、およびその他のソースでは、エラー処理は非常に客観的な科学として示される傾向がexceptions
ありますが、それは解決され、catch
あらゆる状況から回復できる堅牢なソフトウェアが必要です。しかし、開発者としての数年間は、別のアプローチから問題を見るようになりました。
- プログラマは、特定のケースがあまりにもまれに慎重に実装できないと思われる場合に例外をスローすることにより、タスクを単純化する傾向があります。この典型的なケースは次のとおりです。メモリ不足の問題、ディスクの空き容量の問題、破損したファイルの問題など。これで十分かもしれませんが、必ずしもアーキテクチャレベルから決定されるわけではありません。
- プログラマは、ライブラリの例外に関するドキュメントを注意深く読んでいない傾向があり、通常、関数がスローするタイミングとタイミングを認識していません。さらに、たとえ知っていても、実際には管理していません。
- プログラマーは、十分早くに例外をキャッチしない傾向があります。そして、それらをキャッチする場合、ほとんどの場合、ログに記録してさらにスローします。(最初のポイントを参照)。
これには2つの結果があります。
- 頻繁に発生するエラーは、開発の初期段階で検出され、デバッグされます(これは良いことです)。
- まれな例外は管理されず、ユーザーのホームでシステムがクラッシュします(素敵なログメッセージが表示されます)。エラーが報告される場合もあれば、そうでない場合もあります。
それを考慮すると、IMOのエラーメカニズムの主な目的は次のとおりです。
- 特定のケースが管理されていないコードで可視化する。
- この状況が発生した場合、問題のランタイムを関連コード(少なくとも呼び出し元)に伝えます。
- 回復メカニズムを提供します
exception
エラー処理メカニズムとしてのセマンティックの主な欠陥はIMOです。throw
ソースコードのどこにa があるかは簡単にわかりますが、宣言を見ることで特定の関数がスローできるかどうかはわかりません。これは、上で紹介したすべての問題をもたらします。
言語は、言語の他の側面(たとえば、強力なタイプの変数)の場合ほど厳密にエラーコードを適用およびチェックしません。
解決策を試す
これを改善するために、非常に単純なエラー処理システムを開発しました。これは、通常のコードと同じレベルのエラー処理をエラー処理にしようと試みます。
アイデアは次のとおりです。
- 各(関連する)関数は、
success
非常に軽いオブジェクトへの参照を受け取り、場合によってはエラー状態に設定することがあります。オブジェクトは、テキスト付きのエラーが保存されるまで非常に軽いです。 - 提供されたオブジェクトに既にエラーが含まれている場合、関数はそのタスクをスキップすることが推奨されます。
- エラーを無効にしないでください。
完全な設計では、明らかに各側面(約10ページ)を徹底的に検討し、OOPへの適用方法も検討します。
Success
クラスの例:
class Success
{
public:
enum SuccessStatus
{
ok = 0, // All is fine
error = 1, // Any error has been reached
uninitialized = 2, // Initialization is required
finished = 3, // This object already performed its task and is not useful anymore
unimplemented = 4, // This feature is not implemented already
};
Success(){}
Success( const Success& v);
virtual ~Success() = default;
virtual Success& operator= (const Success& v);
// Comparators
virtual bool operator==( const Success& s)const { return (this->status==s.status && this->stateStr==s.stateStr);}
virtual bool operator!=( const Success& s)const { return (this->status!=s.status || this->stateStr==s.stateStr);}
// Retrieve if the status is not "ok"
virtual bool operator!() const { return status!=ok;}
// Retrieve if the status is "ok"
operator bool() const { return status==ok;}
// Set a new status
virtual Success& set( SuccessStatus status, std::string msg="");
virtual void reset();
virtual std::string toString() const{ return stateStr;}
virtual SuccessStatus getStatus() const { return status; }
virtual operator SuccessStatus() const { return status; }
private:
std::string stateStr;
SuccessStatus status = Success::ok;
};
使用法:
double mySqrt( Success& s, double v)
{
double result = 0.0;
if (!s) ; // do nothing
else if (v<0.0) s.set(Error, "Square root require non-negative input.");
else result = std::sqrt(v);
return result;
}
Success s;
mySqrt(s, 144.0);
otherStuff(s);
saveStuff(s);
if (s) /*All is good*/;
else cout << s << endl;
私は自分の(自分の)コードの多くでそれを使用し、プログラマー(私)に例外的な可能性のあるケースとその解決方法(良い)についてさらに考えるように強制しています。ただし、学習曲線があり、現在使用しているコードとはうまく統合できません。
質問
プロジェクトでこのようなパラダイムを使用することの意味をよりよく理解したいと思います。
- 問題の前提は正しいですか?または関連する何かを見逃しましたか?
- ソリューションは優れたアーキテクチャのアイデアですか?または価格が高すぎますか?
編集:
メソッド間の比較:
//Exceptions:
// Incorrect
File f = open("text.txt"); // Could throw but nothing tell it! Will crash
save(f);
// Correct
File f;
try
{
f = open("text.txt");
save(f);
}
catch( ... )
{
// do something
}
//Error code (mixed):
// Incorrect
File f = open("text.txt"); //Nothing tell you it may fail! Will crash
save(f);
// Correct
File f = open("text.txt");
if (f) save(f);
//Error code (pure);
// Incorrect
File f;
open(f, "text.txt"); //Easy to forget the return value! will crash
save(f);
//Correct
File f;
Error er = open(f, "text.txt");
if (!er) save(f);
//Success mechanism:
Success s;
File f;
open(s, "text.txt");
save(s, f); //s cannot be avoided, will never crash.
if (s) ... //optional. If you created s, you probably don't forget it.