次の目的で、.NET フレームワークのものを模倣する Exception クラスを C++ で実装したいと思います (Java にも同様のものがあります)。
例外チェーン: 上位レベルでキャッチされた例外が下位レベルの例外をラップして「変換」し、これらの下位レベルの例外を何らかの形で (
InnerException
この場合はメンバー内で)保持する「例外変換」の概念を実装したいと思います。 . このため、上位レベルでスローされた各例外とともに内部例外を格納するメカニズムが必要です。InnerException
member は、以下の実装でこれを提供します。例外の継承:たとえば、、および
IoException
から派生することが可能であるべきです。これは些細なことのように思えますが、できれば RTTI や.Exception
SerialPortException
IoException
typeid
これは、可能にしたい例外処理ロジックのサンプルです。
try
{
try
{
try
{
throw ThirdException(L"this should be ThirdException");
}
catch(Exception &ex)
{
throw SubException(L"this should be SubException", ex);
}
}
catch(Exception &ex)
{
throw SubException(L"this should be SubException again", ex);
}
}
catch(Exception &ex)
{
throw Exception(L"and this should be Exception", ex);
}
そして、最上層で「最も外側の」例外をキャッチするときに、InnerException
メンバーを介して例外チェーン全体を解析およびフォーマットして、次のようなものを表示できるようにしたいと考えています。
これまでのところ、次の実装を考え出しました。
ちょっとした注意: CString
Microsoft 固有の文字列クラスです (Visual C++ に慣れていない人向けです)。
class Exception
{
protected:
Exception(const Exception&) {};
Exception& operator= (const Exception&) {};
public:
Exception(const CString &message) : InnerException(0), Message(message) {}
Exception(const CString &message, const Exception &innerException) : InnerException(innerException.Clone()), Message(message) {}
virtual CString GetExceptionName() const { return L"Exception"; }
virtual Exception *Clone() const
{
Exception *ex = new Exception(this->Message);
ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;
return ex;
}
public:
virtual ~Exception() { if (InnerException) delete InnerException; }
CString Message;
const Exception *InnerException;
};
さて、ここには何がありますか。protected
コピーを防ぐために、コピーコンストラクターと代入演算子が作成されます。各オブジェクトは内部の例外オブジェクトを「所有」する (そしてデストラクタでそれを削除する) ため、デフォルトの浅いコピーは受け入れられません。次に、2 つの非常に標準的なコンストラクタと、オブジェクトを削除する仮想デストラクタがありInnerException
ます。Clone()
仮想メソッドは、主に内部例外オブジェクトを格納するために、オブジェクトのディープ コピーを担当します (2 番目のコンストラクターを参照)。最後にGetExceptionName()
、仮想メソッドは、例外クラス名を識別するための RTTI の安価な代替手段を提供します (これはクールに見えませんが、より良い解決策を思い付くことができませんでした。比較のために: .NET では単純に を使用できますsomeException.GetType().Name
)。
これでうまくいきます。しかし...私はこのソリューションが1つの特定の理由で好きではありません:各派生クラスに必要なコーディングの量です。基本クラスの機能にまったく追加を提供しないクラスを派生させる必要があると考えてくださいSubException
。それは、その使用法を区別するためにカスタム名 (「SubException」、「IoException」、「ProjectException」など) を提供するだけです。シナリオ。このような例外クラスごとに、ほぼ同じ量のコードを提供する必要があります。ここにあります:
class SubException : public Exception
{
protected:
SubException(const SubException& source) : Exception(source) {};
SubException& operator= (const SubException&) {};
public:
SubException(const CString &message) : Exception(message) {};
SubException(const CString &message, const Exception &innerException) : Exception(message, innerException) {};
virtual CString GetExceptionName() const { return L"SubException"; }
virtual Exception *Clone() const
{
SubException *ex = new SubException(this->Message);
ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;
return ex;
}
};
protected
毎回コピー コンストラクターと代入演算子を提供する必要があるという事実は好きではありません。Clone
毎回メソッドを複製しなければならず、基本メンバーをコピーするコードも複製しなければならないという事実は好きではありません ( InnerException
... )、単純に...これはエレガントなソリューションではないと思います。しかし、私はより良いものを考えることができませんでした. この概念を「適切に」実装する方法はありますか? それとも、これが C++ で可能なこの概念の最良の実装でしょうか? それとも、私はこれを完全に間違っていますか?
PS: C++11 (Boost にも) には、いくつかの新しい例外クラスを使用したこの目的 (例外チェーン) のためのメカニズムがいくつか存在することは知っていますが、主にカスタムの「古い C++ 互換」の方法に興味があります。さらに、誰かが同じことを実現する C++11 のコードを提供できれば、それは良いことです。