Kann ein C-Programm C++-Ausnahmen behandeln?

Lesezeit: 6 Minuten

Benutzer-Avatar
Ahmed

Ich entwickle eine C++-Komponenten-DLL, die von C- oder C++-Anwendungen verwendet werden kann. Die exponierten DLL-Funktionen lauten wie folgt

#include <tchar.h>
#ifdef IMPORT
#define DLL __declspec(dllimport)
#else 
#define DLL __declspec(dllexport)
#endif

extern "C" {

    DLL  bool __cdecl Init();
    DLL  bool __cdecl Foo(const TCHAR*);
    DLL  bool __cdecl Release();

}

Die interne Implementierung dieser Funktionen sind C++-Klassen, die nicht verfügbar gemacht werden. Ich gehe davon aus, dass die DLL mit diesem Stil entweder in C- oder C++-Apps verwendet werden kann. Das Problem ist, dass ich keine Art von C++-Ausnahme (dh bad_alloc) behandle und dieses Zeug dem Aufrufer (der höheren Schicht) überlasse. Nach vielen Diskussionen mit meinen Kollegen, dass ich alle Ausnahmen abfangen und Fehlercode oder zumindest falsch zurückgeben sollte, weil es im Falle einer C-Anwendung keine C++-Ausnahmen verarbeiten kann? ist das wahr? und was soll ich generell machen? Gibt es eine Faustregel für den Umgang mit Ausnahmen, wenn Sie eine Komponente entwickeln, die von einem anderen System verwendet wird?

  • Nein, es gibt keine Ausnahme in C.

    – sje397

    15. Dezember 2010 um 10:05 Uhr

  • on-time.com/ddj0011.htm

    – DumbCoder

    15. Dezember 2010 um 10:11 Uhr

  • C++ kann eine C-Verknüpfung registrieren / extern C Funktion als Ausnahmebehandler für nicht abgefangene Ausnahmen über std::set_unexpected(). Wenn der verstümmelte Name dafür bekannt ist, können Sie ihn direkt aus dem einfachen C aufrufen. Andernfalls können Sie an entlarven extern C Hülle dafür. Nicht, dass dies eine bequeme Schnittstelle wäre, aber wenn es absolut unvermeidlich ist, dass Ihre Bibliothek über ihre eigenen Grenzen hinaus verschmutzt …

    – FrankH.

    15. Dezember 2010 um 10:24 Uhr

  • @FrankH: Dies sollte eine Antwort sein, kein Kommentar. Viele Bibliotheken (z. B. alte Fortran-Bibliotheken) verwenden den Handler-Ansatz.

    – Alexander C.

    15. Dezember 2010 um 10:46 Uhr

  • @DumbCoder: Mach das nicht im echten Code.

    – Alexander C.

    15. Dezember 2010 um 10:47 Uhr

C hat keine Ausnahmen, daher sollten Sie im Allgemeinen alle Ausnahmen abfangen und einen Fehlercode zurückgeben und/oder eine Funktion bereitstellen, die die Informationen über den letzten Fehler zurückgibt.

  • “C hat keine Ausnahmen”. Warum nicht; Die C++-Laufzeit hat Ausnahmen, und die C++-Laufzeit hat die C++-Laufzeit, also warum kann C die C++-Laufzeit nicht für eine bestimmte Implementierung von Ausnahmen abfangen (verschiedene Systeme haben möglicherweise eine andere Implementierung, aber die Implementierung ist immer noch vorhanden und muss vorhanden sein in Speicher, und selbst wenn er nicht exponiert ist, können Sie den Speicher analysieren, um ihn herauszufinden und verschiedene Bytes zu stoßen und nicht exponierte Funktionen aufzurufen).

    – Dmitri

    29. November 2016 um 23:26 Uhr


Wenn dies Windows ist, das MSVC verwendet, können Sie Ausnahmen in C abfangen, aber Sie können sie nicht so gut abfangen. C++-Ausnahmen werden über den strukturierten Ausnahmebehandlungsmechanismus des Betriebssystems ABI zugeführt, und Microsoft hat eine __versuchen, __außer, __endlich C-Erweiterung zur Behandlung von OS-strukturierten Ausnahmen. Beachten Sie, dass diese Zugriffsverletzungen, Division durch Null usw. umfassen, die Sie normalerweise Ihr Programm beenden und einen Fehlerbericht protokollieren möchten. Sie können C++-Ausnahmen anhand des Codes 0xE04D5343 (4D 53 43 = “MSC”) identifizieren und den Rest anwerfen.

Alles in allem möchten Sie wahrscheinlich keine Ausnahmen über eine DLL-Grenze hinweg auslösen, und schon gar nicht, wenn Sie nur eine C-API verfügbar machen.

  • Ok, aber wenn ich der Client der DLL C++-Apps bin, ist es dann eine gute Idee, Ausnahmen über DLL-Grenzen hinweg zu verbreiten?

    – Ahmed

    15. Dezember 2010 um 10:21 Uhr

  • @Ahmed: Nein, es sei denn, Sie möchten DLLs für jede verfügbare Compiler-Version bereitstellen.

    – Georg Fritzsche

    15. Dezember 2010 um 10:24 Uhr

  • @Ahmed: In diesem Fall würde ich sagen, dass es so ist, aber dann ist es einfach –> Wenn Sie eine C-Schnittstelle bereitstellen, verwenden Sie keine Ausnahmen, wenn Sie eine C++-Schnittstelle bereitstellen, können Sie dies tun.

    – Matthias M.

    15. Dezember 2010 um 10:25 Uhr

  • @Georg: Ah, ich gehe immer davon aus, dass der Client die Quelle neu kompiliert; p

    – Matthias M.

    15. Dezember 2010 um 10:25 Uhr

In der Regel sollten Sie noch nie Ermöglichen Sie C++-Ausnahmen, sich über die Grenzen eines Moduls hinaus auszubreiten. Dies liegt daran, dass der C++-Standard nicht angibt, wie die Ausnahmeweitergabe implementiert werden muss, und dies daher vom Compiler (und Compiler-Flags) und vom Betriebssystem abhängig ist. Sie können nicht garantieren, dass der Code, der Ihr Modul aufruft, mit demselben Compiler mit denselben Compiler-Flags wie Ihr Modul kompiliert wird. Wie Sie mit dieser Frage demonstrieren, können Sie nicht garantieren, dass der Code, der Ihr Modul aufruft, in derselben Sprache geschrieben wird.

Weitere Einzelheiten finden Sie in Punkt 62 in C++ Coding Standards von Sutter und Alexandrescu.

Ok, da gefragt wurde:

C++-Beispielcode:

#include <typeinfo>
#include <exception>
extern "C" {
void sethandler(void (*func)(void)) { std::set_terminate(func); }
int throwingFunc(int arg) {
    if (arg == 0)
        throw std::bad_cast();
    return (arg - 1);
}
}

C-Beispielcode:

#include <stdio.h>

extern int throwingFunc(int arg);
extern void sethandler(void (*func)(void));

void myhandler(void)
{
    printf("handler called - must've been some exception ?!\n");
}

int main(int argc, char **argv)
{
    sethandler(myhandler);

    printf("throwingFunc(1) == %d\n", throwingFunc(1));
    printf("throwingFunc(-1) == %d\n", throwingFunc(-1));
    printf("throwingFunc(0) == %d\n", throwingFunc(0));
    return 0;
}

Wenn ich diese beiden kompiliere, sie miteinander verknüpfe und dies ausführe (Ubuntu 10.04, gcc 4.4.5, x64), erhalte ich:

$ ./xx
throwingFunc(1) == 0
throwingFunc(-1) == -2
handler called - must've been some exception ?!
Aborted

Also während du kann Ausnahmen von C abfangen, das reicht kaum aus – weil C++ Laufzeitverhalten nach std::terminate() undefiniert ist und weil der Handler keinerlei Statusinformationen erhält (um Ausnahmetypen und/oder -quellen zu unterscheiden). Der Handler kann nichts aufräumen.

Übrigens, ich habe mich bewusst entschieden std::bad_cast() als Ausnahmetyp in diesem Beispiel. Dies liegt daran, dass das Werfen einen Unterschied im Verhalten zwischen veranschaulicht std::set_unexpected() und std::set_terminate() – Der unerwartete Handler wird für alle Non aufgerufen std::bad_* Ausnahmen, während zum Abfangen von Standardausnahmen ein Beendigungshandler erforderlich ist … sehen Sie das Dilemma? Das Geschirr ist zu breit, um von praktischem Nutzen zu sein 🙁

Benutzer-Avatar
scharfer Zahn

Geben Sie Ausnahmen nur dann an den Aufrufer weiter, wenn der Aufrufer dafür ausgelegt ist, sie zu behandeln. Ich denke in deinem Fall terminate() wird sofort aufgerufen, sobald eine Ausnahme C++-Code maskiert, da diese Ausnahme aus Sicht der C++-Laufzeit nicht behandelt wurde.

Die gleiche Situation tritt beim Design von COM-Servern auf – die Clients können in jeder Sprache/Technologie sein. Die Regel ist, dass keine Ausnahmen COM-Servermethoden entkommen sollten – alle Ausnahmen müssen abgefangen und in HRESULT übersetzt werden und (optional) IFehlerInfo. Sie sollten in Ihren Situationen ähnlich vorgehen.

Falls C-Code zwischen zwei Ebenen von C++-Code eingebettet ist, ist das Propagieren von Ausnahmen an C-Code immer noch eine sehr schlechte Idee.

1144520cookie-checkKann ein C-Programm C++-Ausnahmen behandeln?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy