2010-07-26 11 views
9

SIGFPE'ye (sinyali() geçirilen bir işlev işaretçisi tarafından ele alınan birkaç bin satırlık bir uygulamaya sahibim. ve belirli kayan nokta koşulları gerçekleştiğinde kodun doğru şekilde çalışmasını sağlayın. Ancak, yönetilen modda C++/CLI altında, _control87, C. _fpreset ve _control87'de yazılan bir statik kitapta çalıştırılan bir System.ArithmeticException oluşturur.C++/CLI: SIGFPE, _control87, _fpreset, eski yönetilmeyen Watcom C uygulamasını .NET'e taşıyor

C++/CLI uygulamasında çalışmak üzere klasik, yönetilmeyen SIGFPE işlemini nasıl alabilirim? Uygulamamda kayan noktaların meydana geldiği yerlerin sayısı muazzam olabilir ve yıllar önce diğer programcılar tarafından yazılan tüm sayısal yöntemleri tam olarak anlayamıyorum.

Bir INF değeri değil, kayan nokta bölümlemesi üzerinde çalışmak için eski okul istisnası işlemesini istiyorum. Platform çağırma stili çalışmıyor ve #pragma yönetiliyor (kapalı) ya hile yapmıyor.

Hangi seçeneklerim var?

+0

/clr olmadan derlendiğinizde çalışıyor mu? Uygulamayı çağırmak için diğer yönetilen kodunuzun/veya clr kısmının (veya yönetilen malzemelerin aranması için) ve yerel parçanın SIGPFE ile bölünmesini sağlar mısınız? Yoksa çok dolaşmışlar mı? –

+0

C# interop ile benzer bir durumum var. C++/CLI kodum yok, tüm C++ kodlarım yönetilmiyor. SIGFPE için geri çağrımı kaydediyorum (yönetilmeyen kod için calltack almaya çalışırken), ancak .NET çalışma zamanı her zaman sinyal işlevimi çağırmak yerine ArithmeticException'ı geçersiz kılar ve atar. – zahir

cevap

4

Burada çok ciddi ağrı noktaları vardır. Kayan nokta istisnalarının etkinleştirilmesi, yönetilen kod yürütme ile büyük ölçüde uyumsuzdur. Temel bilgilere göre, JIT-derleyicisini kolayca kilitleyebilirsiniz. _control87() işlevini kullanırken karşılaştığınız sorun hangisidir?

Ve evet, bir CLR istisnası alırsınız, yerel kodu yürüttüğünde, bir istisna geri döndürmez duruma geçer. Bir sinyal işleyici sadece istisna kaldırıldığında çağrılır ve işlemek için kod yoktur. Kaçınılmaz olarak CLR, C çalışma zamanı kitaplığı onu görmeden önce istisna görür. Bu yüzden SIGFPE işleyici çağrısını asla alamazsınız.

Bu konuda bir çekim yapmak için tek iyi yolu, CLR'nin yapamayacağı özel durumu yakalayan bir sarmalayıcı yazmaktır. Ayrıca, FPU kontrol sözcüğünü dikkatli bir şekilde yönetmeniz çok önemlidir, ancak yerel kod çalışırken sadece FPU istisnalarının etkin olmasını sağlayabilirsiniz. Bu, bir sürü cesur kod alır, yukarı-ön uyarısı çok fazla zevk almayacağınızı gösterir.

Garip bir örnek oluşturan gerekecek böylece herhangi pasajını gönderilmeyen:

denilen fpehandler() elde etmek için
#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) 

, C çalışma zamanı içindeki istisna işleyicisi çağırmanız gerekir kütüphane. Bunu sadece hiç nokta istisnalar kayan çağrısında emin olmak gerekir

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

: Neyse ki maruz kaldığı ve bunu bağlayabilirsiniz o kadar arayabilirsin için, sadece bir bildiri gerekir.

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

sırayla çağrılabilir: denilen sinyal işleyici alır

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

Şimdi badmath (için bir sarıcı yazabilirsiniz): Yani biz istisna koduna dikkat eden sarmalayıcı gerek Yönetilen herhangi bir koddan arayabileceğiniz bir C++/CLI sınıfı. Bu kayan nokta istisnalar çağrısından önce etkin ve çağrıdan sonra tekrar döndürülmesini sağlamak gerekir:

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

Not çağrı, bu "hatalı sonuç" dışındaki tüm yüzen istisnaları sağlayan) (_control87 için. Kodun takılmasına izin vermek için bu gereklidir. Eğer maskelenmezseniz, CLR korkunç bir ölümle sonuçlanır ve bu sitenin adı sona erene kadar istisnalar tekrar tekrar atılır. Umarım sinyal işleyicinizin buna ihtiyacı yoktur.

İlgili konular