2010-07-26 5 views
9

Ich habe eine mehrere tausend-line-Anwendung, die auf SIGFPE beruht (von einem Funktionszeiger an signal() übergeben), um den Zustand zu ändern und der Code wird korrekt ausgeführt, wenn bestimmte Gleitkommazustände auftreten. Unter C++/CLI im verwalteten Modus generiert _control87 jedoch eine System.ArithmeticException, die in einer in C geschriebenen statischen Bibliothek ausgeführt wird. _fpreset und _control87 werden nicht unterstützt.C++/CLI: SIGFPE, _control87, _fpreset, Portierung alten unmanaged Watcom C App zu. NET

Wie bekomme ich klassische, nicht verwaltete SIGFPE-Operation in einer C++/CLI-Anwendung arbeiten? Die Anzahl der Orte, an denen Gleitkomma-Sachen in meiner Anwendung passieren, könnte immens sein und ich verstehe nicht alle numerischen Methoden, die vor Jahren von anderen Programmierern geschrieben wurden.

Ich möchte Old-School-Ausnahmebehandlung auf eine Gleitkommadivision von Null arbeiten, nicht ein INF-Wert. Platform Invoke Style funktioniert nicht, und #pragma managed (off) macht auch keinen Trick.

Welche Optionen habe ich?

+0

Funktioniert es, wenn Sie ohne/clr kompilieren? Könnten Sie die App in den/clr-Teil aufteilen, damit Ihr anderer verwalteter Code (oder von Ihnen aus verwaltete Sachen genannt) und der native Teil mit dem SIGPFE aufrufen? Oder sind sie zu verwickelt? –

+0

Ich habe eine ähnliche Situation mit C# Interop über reine C-Interace. Ich habe keinen C++/CLI-Code, mein C++ - Code ist nicht verwaltet. Ich registriere meinen Rückruf für SIGFPE (versuchen, Callstack für nicht verwalteten Code zu erwerben), aber .NET-Laufzeit überschreibt immer und löst ArithmeticException, anstatt meine Signalfunktion aufzurufen. – zahir

Antwort

4

Es gibt mehrere sehr ernste Schwachstellen hier. Das Aktivieren von Gleitkommaausnahmen ist grob mit der Ausführung von verwaltetem Code nicht kompatibel. Bis auf die Grundlagen können Sie den JIT-Compiler problemlos zum Absturz bringen. Mit welchem ​​Problem kämpfen Sie, wenn Sie _control87() verwenden?

Und ja, Sie erhalten eine CLR-Ausnahme, es setzt eine Ausnahme Backstop in Stelle, wenn es nativen Code ausführt. Ein Signal-Handler wird nur dann aufgerufen, wenn eine Ausnahme ausgelöst wird und es keinen Code gibt, um damit umzugehen. Unvermeidlicherweise sieht die CLR die Ausnahme, bevor die C-Laufzeitbibliothek sie sehen kann. So erhalten Sie den SIGFPE-Handler-Aufruf nie.

Der einzige vernünftige Weg, um einen Schuss zu bekommen, ist einen Wrapper schreiben, der die Ausnahme vor der CLR abfangen kann. Außerdem ist es sehr, sehr wichtig, dass Sie das FPU-Steuerwort sorgfältig verwalten. Sie können sich nur erlauben, FPU-Ausnahmen aktiviert zu haben, während der native Code läuft. Dies erfordert eine Reihe von grobkörnigem Code, der davor warnt, dass Sie es nicht sehr genießen werden.

Sie haben keine Schnipsel schreiben, damit ich ein dummes Beispiel bilden müssen werden:

#include <Windows.h> 
#include <signal.h> 
#include <float.h> 

#pragma managed(push, off) 

double divisor; 

void __cdecl fpehandler(int sig) { 
    divisor = 1.0; 
} 

double badmath() { 
    divisor = 0.0; 
    return 1/divisor; 
} 
#pragma managed(pop) 

Um fpehandler() aufgerufen zu bekommen, müssen Sie die Exception-Handler in der C-Laufzeit nennen Bibliothek. Zum Glück es ausgesetzt ist, und Sie können es verknüpfen, müssen Sie nur eine Erklärung dafür, so kann man es nennen:

// Exception filter in the CRT, it raises the signal 
extern "C" int __cdecl _XcptFilter(unsigned long xcptnum, 
            PEXCEPTION_POINTERS pxcptinfoptrs); 

Sie müssen sicherstellen, dass es immer nur für Gleitkomma-Ausnahmen genannt wird. Also brauchen wir einen Wrapper, der die Aufmerksamkeit auf den Ausnahmecode zahlt:

int FloatingpointExceptionFilter(unsigned long xcptnum, PEXCEPTION_POINTERS pxcptinfoptrs) { 
    // Only pass floating point exceptions to the CRT 
    switch (xcptnum) { 
     case STATUS_FLOAT_DIVIDE_BY_ZERO: 
     case STATUS_FLOAT_INVALID_OPERATION: 
     case STATUS_FLOAT_OVERFLOW: 
     case STATUS_FLOAT_UNDERFLOW: 
     case STATUS_FLOAT_DENORMAL_OPERAND: 
     case STATUS_FLOAT_INEXACT_RESULT: 
     case STATUS_FLOAT_STACK_CHECK: 
     case STATUS_FLOAT_MULTIPLE_TRAPS: 
     case STATUS_FLOAT_MULTIPLE_FAULTS: 
      return _XcptFilter(xcptnum, pxcptinfoptrs); 
      break; 
     default: 
      return EXCEPTION_CONTINUE_SEARCH; 
    } 
} 

Jetzt können Sie einen Wrapper für badmath write(), die die Signal-Handler wird aufgerufen:

double badmathWrapper() { 
    __try { 
     return badmath(); 
    } 
    __except (FloatingpointExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { 
    } 
} 

Was wiederum aufgerufen werden kann von einer C++/CLI-Klasse, die Sie von jedem verwalteten Code aufrufen können. Es muss sicherstellen, dass Gleitkomma-Ausnahmen vor dem Aufruf aktiviert werden und wieder nach dem Aufruf gestellt:

using namespace System; 
using namespace System::Runtime::CompilerServices; 

public ref class Wrapper { 
public: 
    static double example(); 
}; 

[MethodImplAttribute(MethodImplOptions::NoInlining)] 
double Wrapper::example() { 
    signal(SIGFPE, fpehandler); 
    _clear87(); 
    unsigned oldcw = _control87(_EM_INEXACT, _MCW_EM); 
    try { 
     return badmathWrapper(); 
    } 
    finally { 
     _control87(oldcw, _MCW_EM); 
     signal(SIGFPE, nullptr); 
    } 
} 

Hinweis des Aufruf an _control87(), ermöglicht es, alle schwebenden Ausnahmen mit Ausnahme von „ungenauem Ergebnis“. Dies ist notwendig, um den Code zu sperren. Wenn du es nicht maskierst, dann stirbt die CLR einen schrecklichen Tod und wirft Ausnahmen immer und immer wieder, bis der Name dieser Seite ihr ein Ende setzt. Hoffentlich braucht Ihr Signal-Handler es nicht.