Richtige/elegante Art und Weise, die C++-Ausnahmeverkettung zu implementieren?C++

Programme in C++. Entwicklerforum
Anonymous
 Richtige/elegante Art und Weise, die C++-Ausnahmeverkettung zu implementieren?

Post by Anonymous »

Ich möchte eine Exception-Klasse in C++ implementieren, die die aus dem .NET Framework nachahmt (und Java hat auch etwas Ähnliches), für die folgenden Zwecke:
  • Ausnahmeverkettung: Ich möchte das Konzept der „Ausnahmeübersetzung“ implementieren, wenn auf höheren Ebenen abgefangene Ausnahmen die Ausnahmen auf niedrigerer Ebene umschließen und „übersetzen“, wobei diese Ausnahmen auf niedrigerer Ebene auch irgendwie erhalten bleiben (in diesem Fall im InnerException-Member). Zu diesem Zweck sollte es einen Mechanismus geben, um innere Ausnahmen zusammen mit jeder auf der oberen Ebene ausgelösten Ausnahme zu speichern. Das InnerException-Mitglied stellt dies in der folgenden Implementierung bereit.
  • Ausnahmevererbung: Es sollte beispielsweise möglich sein, IoException von Exception und SerialPortException von IoException abzuleiten. Obwohl dies trivial erscheint, sollte es möglich sein, die Art der abgefangenen Ausnahmen dynamisch zu identifizieren (z. B. für Protokollierungszwecke oder zur Anzeige für den Benutzer), vorzugsweise ohne den Mehraufwand von RTTI und Typ-ID.
Dies ist die Beispiellogik für die Ausnahmebehandlung, die ich ermöglichen möchte:

Code: Select all

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);
}
und wenn ich die „äußerste“ Ausnahme in der obersten Ebene abfange, möchte ich in der Lage sein, die gesamte Ausnahmekette über das InnerException-Mitglied zu analysieren und zu formatieren, um etwa Folgendes anzuzeigen:
Image

Ich habe mir also die folgende Implementierung ausgedacht weit:
Kleiner Hinweis: CString ist eine Microsoft-spezifische String-Klasse (nur für Leute, die mit Visual C++ nicht vertraut sind).

Code: Select all

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;
};
Was haben wir hier? Kopierkonstruktor und Zuweisungsoperator werden geschützt, um das Kopieren zu verhindern. Jedes Objekt „besitzt“ sein inneres Ausnahmeobjekt (und löscht es im Destruktor), sodass ein standardmäßiges flaches Kopieren nicht akzeptabel wäre. Dann haben wir zwei ziemlich normal aussehende Konstruktoren und einen virtuellen Destruktor, der das InnerException-Objekt löscht. Die virtuelle Methode Clone() ist für das Tiefenkopieren der Objekte verantwortlich, hauptsächlich für die Speicherung des inneren Ausnahmeobjekts (siehe den zweiten Konstruktor). Und schließlich bietet die virtuelle Methode GetExceptionName() die kostengünstige Alternative zu RTTI zur Identifizierung von Ausnahmeklassennamen (ich finde das nicht cool, aber ich könnte mir keine bessere Lösung einfallen lassen; zum Vergleich: In .NET könnte man einfach someException.GetType().Name verwenden).
Jetzt erledigt das den Job. Aber ... diese Lösung gefällt mir aus einem bestimmten Grund nicht: der Menge an Codierung, die für jede abgeleitete Klasse erforderlich ist. Bedenken Sie, dass ich die SubException-Klasse ableiten muss, die absolut keine Ergänzungen zur Basisklassenfunktionalität bereitstellt. Sie stellt lediglich den benutzerdefinierten Namen („SubException“, der möglicherweise „IoException“, „ProjectException“ usw. sein kann) bereit, um sie für ihr Verwendungsszenario zu unterscheiden. Ich muss für jede dieser Ausnahmeklassen fast die gleiche Menge Code bereitstellen. Hier ist es:

Code: Select all

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;
}
};
Mir gefällt die Tatsache nicht, dass ich jedes Mal einen geschützten Kopierkonstruktor und Zuweisungsoperator bereitstellen muss. Mir gefällt die Tatsache nicht, dass ich jedes Mal die Clone-Methode klonen muss, wodurch sogar der Code zum Kopieren der Basismitglieder dupliziert wird (

Code: Select all

InnerException
...), einfach... Ich glaube nicht, dass das die elegante Lösung ist. Aber ich konnte mir kein besseres vorstellen. Habt ihr Ideen, wie man dieses Konzept „richtig“ umsetzen kann? Oder ist dies vielleicht die beste Implementierung dieses Konzepts, die in C++ möglich ist? Oder mache ich das vielleicht völlig falsch?
P.S.: Ich weiß, dass es in C++11 (auch in Boost) einige Mechanismen für diesen Zweck (Ausnahmeverkettung) mit einigen neuen Ausnahmeklassen gibt, aber ich interessiere mich hauptsächlich für benutzerdefinierte, „mit altem C++ kompatible“ Methoden. Aber es wäre darüber hinaus gut, wenn jemand Code in C++11 bereitstellen könnte, der dasselbe erreicht.

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post