2009-05-11 9 views
15

Ich entwickle ein CAPI für einige in C++ geschriebene Funktionalität, und ich möchte sicherstellen, dass keine Ausnahmen aus den exportierten C-Funktionen propagiert werden.Codewiederverwendung bei der Ausnahmebehandlung

Der einfache Weg, es ist dafür, dass jede exportierte Funktion in einem tun enthalten ist:

try { 
    // Do the actual code 
} catch (...) { 
    return ERROR_UNHANDLED_EXCEPTION; 
} 

Sagen wir, ich eine Ausnahme kennen, die oft in der C verpasst ++ Code ist std :: bad_alloc und ich möchte so etwas zu behandeln, es speziell würde ich stattdessen schreiben:

try { 
    // Run the actual code 
} catch (std::bad_alloc& e) { 
    return ERROR_BAD_ALLOC; 
} catch (...) { 
    return ERROR_UNHANDLED_EXCEPTION; 
} 

Ist es möglich, dies in einigen cleveren Weg zu zersetzen, so dass ich global für die Exception-Handler behandeln können einige Fehler anders, ohne eine neue catch-Anweisung Hinzufügen um jede exportierte Funktion?

Ich bin mir bewusst, dass dies mit dem Präprozessor gelöst werden kann, aber bevor ich diesen Weg gehe, würde ich sicherstellen, dass es keinen anderen Weg gibt, es zu tun.

Antwort

27

Sie können nur eine Behandlungsfunktion für alle möglichen Ausnahmen verwenden, und nennen Sie es von jedem oder Ihre API-Implementierung Funktionen, wie unten:

int HandleException() 
{ 
    try 
    { 
     throw; 
    } 

    // TODO: add more types of exceptions 

    catch(std::bad_alloc &) 
    { 
     return ERROR_BAD_ALLOC; 
    } 
    catch(...) 
    { 
     return ERROR_UNHANDLED_EXCEPTION; 
    } 
} 

Und in jedem exportierte Funktion:

try 
{ 
    ... 
} 
catch(...) 
{ 
    return HandleException(); 
} 
+1

+1: Gute Idee :-) –

+0

Perfekt gearbeitet. Vielen Dank! – Laserallan

+2

Im echten Code vergessen Sie nicht, Ausnahmen durch Verweis abzufangen: catch (std :: bad_alloc &) – Jem

0

Verwenden Sie niemals catch(...), es sei denn, Sie planen mehr oder weniger sofort erneut werfen. Sie werden sicherlich alle Fehlerinformationen verloren haben, die Sie möglicherweise hatten, um die Ursache des Fehlers zu finden.

Ich mag Ihr zweites Schema ein wenig besser - fangen Sie eine bekannte Reihe von Ausnahmen, idealerweise weil sie die einzigen sind, die Ihr Code werfen wird, und lassen Sie den Rest durch - ermöglicht die App zum Absturz ist möglicherweise die beste Sache zu tun Da Sie unbekanntes Verhalten angerufen haben, ist es am besten, "verantwortlich zu stürzen".

+4

In diesem Fall denke ich, die Verwendung von Fang (...) ist die richtige Sache zu tun.In C++ ist es schwierig, wenn nicht unmöglich, alle auslösbaren Ausnahmen vorherzusagen, aber er muss verhindern, dass sie sich in seinen C-Code ausbreiten. –

+0

Wenn Sie dies tun, dann planen Sie eine Reihe von schlechten Bug-Berichte mit wenig oder gar keine Informationen darüber, was das Problem verursacht und keine Möglichkeit, mehr –

+0

So würden Sie lieber das Programm abgestürzt, weil einige obskure Bibliothek Sie um zwei abhängen entfernt, entscheidet, dass es MyWeirdError werfen wird? Ich denke, ich würde lieber "Unidentified problem with operation XXX" im Log sehen. –

1

Was:

try{ 
    //Your code here 
} catch(std::exception e) 
{ 
    return translateExceptionToErrorCode(e); 
} catch(...) 
{ 
    return UNKNOWN_EXCEPTION_THROWN; 
} 
+2

Normalerweise würden Sie eine Ausnahme durch Verweis abfangen, um unnötiges Kopieren zu vermeiden. –

+3

Ganz zu schweigen von Slicing. –

+0

Sehr gute Punkte. Behandle meinen Code als Pseudo;) – PaulJWilliams

0

Es wäre eine Schande, Fehlerinformationen in der Sprache bo zu verlieren unpersönlich. Sie sollten wirklich versuchen, alle Ausnahmen in einen von C. verwendbaren Fehlercode zu übersetzen.

Wie Sie es wirklich tun, hängt davon ab, wie Ihre Ausnahmeklassen aussehen. Wenn Sie Ihre Ausnahmeklassenhierarchie steuern, können Sie sicherstellen, dass jede Klasse eine Übersetzung mithilfe einer virtuellen Methode bereitstellt. Wenn nicht, kann es immer noch nützlich sein, eine Übersetzerfunktion zu verwenden und die Typen der von 'std :: exception' abgeleiteten Ausnahme zu testen, um sie in einen Fehlercode zu übersetzen, ähnlich wie Jem vorgeschlagen hat (beachte: ausgelöste Ausnahmen werden weh tun) Leistung sowieso, also mach dir keine Sorgen, dass die Übersetzung langsam ist).

+3

Das Übersetzen der Ausnahmen auf Fehlercodes scheint genau das zu sein, was er tut! –

1

Jem Antwort ist ein wenig einfacher als diese Lösung. Es ist jedoch möglich, die Verwendung eines Präprozessor-Makros durch die Verwendung von Vorlagen zu ersetzen. Etwas ähnliches (mehr Verfeinerungen, die Sie machen konnten):

template <class T, void (T::*FUNC)()> 
class CatchWrapper 
{ 
public: 

    static void WrapCall(T* instance) 
    { 
     try 
     { 
      (instance->*FUNC)(); 
     } 
     catch (std::bad_alloc&) 
     { 
      // Do Something 1 
     } 
     catch (std::exception& e) 
     { 
      // Do Something 2 
     } 
     catch (...) 
     { 
      // Do Something 3 
     } 
    } 
}; 


class Foo 
{ 
public: 
    void SomeCall() 
    { 
     std::cout << "Do Something" << std::endl; 
    } 
}; 


int main(int argc, char* argv[]) 
{ 
    Foo i; 
    CatchWrapper<Foo, &Foo::SomeCall>::WrapCall(&i); 
    return 0; 
} 
+0

das habe ich auch zuerst, (und tatsächlich, was ich in einigen meiner Projekte verwendet habe). Aber Jem's Anwser ist aus meiner Sicht sauberer. Während es mich nicht stört, verwenden manchmal Vorlagen andere Programmierer stört ..:/ –

4

Es gibt bereits eine gute Antwort. Aber nur FYI, es heißt 'Ausnahme-Dispatcher' Idiom, siehe C++ FAQ 17.15. IMHO 17.16 ist auch eine wichtige Lektüre.

+0

Danke, das war super zu lesen. – Laserallan

+0

Es ist jetzt [17.15] [http://www.parashift.com/c%2B%2B-faq-lite/throw-without-an-object.html] – baruch

+0

@ baruch: Danke, auf neue URL korrigiert. – Abhay