2010-07-26 12 views
9

Mam aplikację z kilkoma tysiącami linii, która polega na SIGFPE (obsługiwanym przez wskaźnik funkcji przekazywany do sygnału()), aby zmienić stan i aby kod działał poprawnie, gdy wystąpią pewne warunki zmiennoprzecinkowe. Jednak w C++/CLI w trybie zarządzanym _control87 generuje Wyjątek System.ArithmeticException wykonywany w statycznej bibliotece zapisanej w C. _fpreset i _control87 nie są obsługiwane.C++/CLI: SIGFPE, _control87, _fpreset, przenoszenie starszej niezarządzanej aplikacji Watcom C do .NET

Jak uzyskać klasyczną, niezarządzaną operację SIGFPE do pracy w aplikacji C++/CLI? Liczba miejsc, w których dane zmiennoprzecinkowe występują w mojej aplikacji może być ogromna i nie w pełni rozumiem wszystkie metody numeryczne napisane przed laty przez innych programistów.

Chcę, aby obsługa wyjątków w oldschoolach działała na podziałach zmiennoprzecinkowych o zero, a nie na wartość INF. Styl wywoływania platformy nie działa, a #pragma managed (off) również nie działa.

Jakie mam opcje?

+0

Czy to działa, gdy kompilujesz bez/clr? Czy podzielisz aplikację na część/clr, aby twój drugi zarządzany kod zadzwonił (lub by zadzwonić do zarządzanych rzeczy), a natywna część z SIGPFE? Czy też są zbyt uwikłani? –

+0

Mam podobną sytuację z C# interop za pośrednictwem czystego C interace. Nie mam kodu C++/CLI, cały mój kod C++ jest niezarządzany. Rejestruję swoje wywołanie zwrotne dla SIGFPE (próbuję uzyskać callstack dla niezarządzanego kodu), ale środowisko wykonawcze .NET zawsze nadpisuje i generuje wyjątek ArithmeticException zamiast wywoływać funkcję sygnału. – zahir

Odpowiedz

4

Istnieje kilka bardzo poważnych problemów związanych z bólem. Włączanie wyjątków zmiennoprzecinkowych jest rażąco niezgodne z wykonywaniem zarządzanego kodu. Aż do podstaw, możesz łatwo zawiesić kompilator JIT. Na czym polega problem z korzystaniem z _control87().

I tak, otrzymasz wyjątek CLR, umieszcza on wyjątek w trybie backstop, gdy wykonuje kod natywny. Program obsługi sygnału jest wywoływany tylko wtedy, gdy zgłoszony jest wyjątek i nie ma kodu, który mógłby go obsłużyć. Nieuchronnie CLR widzi wyjątek, zanim biblioteka wykonawcza C go zobaczy. Więc nigdy nie uzyskasz połączenia z obsługą SIGFPE.

Jedynym dobrym sposobem na zrobienie zdjęcia jest napisanie opakowania, które przechwytuje wyjątek, zanim CLR może. Bardzo ważne jest również, aby ostrożnie zarządzać słowem kontrolnym FPU, możesz pozwolić sobie na wyłączenie wyjątków FPU podczas działania natywnego kodu. To wymaga kilku ostrych kodów, z góry ostrzegających, że nie spodoba ci się to bardzo.

Nie pisać żadnych fragment więc muszę nadrobić głupie przykład:

#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) 

W celu uzyskania fpehandler() nazywa, trzeba zadzwonić do obsługi wyjątku wewnątrz C starcie biblioteka. Na szczęście jest on narażony i można je połączyć, trzeba tylko deklarację bo tak można nazwać to:

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

Musisz upewnić się, że jest tylko kiedykolwiek wezwał do pływający punkt wyjątki. Więc musimy owijkę, która zwraca uwagę na kodzie wyjątek:

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; 
    } 
} 

Teraz można napisać otoki dla badmath(), która pobiera obsługi sygnału o nazwie:

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

co z kolei może zostać wywołana według klasy C++/CLI, którą możesz wywołać z dowolnego zarządzanego kodu. Trzeba upewnić się, że floating point wyjątki są włączone przed wywołaniem i przywrócony ponownie po zakończeniu rozmowy:

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); 
    } 
} 

Uwaga wywołanie _control87(), umożliwia wszystkie unoszące się wyjątki wyjątkiem „wyniku niedokładnego”. Jest to konieczne, aby umożliwić skasowanie kodu. Jeśli go nie zamaskujesz, CLR zginie straszliwą śmierć, wyrzucając wyjątki w kółko, dopóki nazwa tej strony nie położy jej końca. Mam nadzieję, że twój program obsługi sygnału nie potrzebuje go.