C++/CLI:SIGFPE、_control87、_fpreset,将古老的非托管 Watcom C 应用程序移植到 .NET
我有一个几千行的应用程序,它依赖 SIGFPE(由传递给 signal() 的函数指针处理)来更改状态,并在发生某些浮点条件时使代码正确运行。但是,在托管模式下的 C++/CLI 下,_control87 会生成在用 C 编写的静态库中执行的 System.ArithmeticException。不支持 _fpreset 和 _control87。
如何在 C++/CLI 应用程序中使用经典的非托管 SIGFPE 操作?我的应用程序中发生浮点内容的位置数量可能是巨大的,而且我并不完全理解其他程序员几年前编写的所有数值方法。
我希望老式的异常处理能够处理浮点除以零,而不是 INF 值。平台调用风格不起作用,#pragma Managed(off) 也不起作用。
我有什么选择?
I have a several-thousand-line application that relies on SIGFPE (handled by a function pointer passed to signal()) to change state and have the code run correctly when certain floating point conditions happen. However, under C++/CLI in managed-mode, _control87 generates a System.ArithmeticException executing in a static lib written in C. _fpreset and _control87 are not supported.
How do I get classic, unmanaged SIGFPE operation to work in a C++/CLI application? The number of locations where floating point stuff happens in my application could be immense and I do not fully understand all of the numerical methods written years ago by other programmers.
I want old-school exception handling to work on a floating point division by zero, not an INF value. Platform invoke style doesn't work, and #pragma managed(off) doesn't do the trick either.
What options do I have?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这里有几个非常严重的痛点。启用浮点异常与托管代码执行完全不兼容。从根本上讲,您很容易使 JIT 编译器崩溃。这是您在使用 _control87() 时遇到的问题。
是的,您会得到一个 CLR 异常,它会在执行本机代码时放置一个异常后备程序。仅当引发异常并且没有代码来处理它时才会调用信号处理程序。 CLR 不可避免地会先于 C 运行时库发现异常。所以你永远不会得到 SIGFPE 处理程序调用。
解决此问题的唯一不错的方法是编写一个包装器,在 CLR 之前捕获异常。另外,仔细管理 FPU 控制字也非常非常重要,您只能在本机代码运行时启用 FPU 异常。这需要一堆坚韧的代码,预先警告你不会非常喜欢它。
您没有发布任何代码片段,因此我必须编写一个愚蠢的示例:
为了调用 fpehandler(),您需要调用 C 运行时库内的异常处理程序。幸运的是,它是公开的,您可以链接它,您只需要一个声明,以便您可以调用它:
您需要确保仅在浮点异常时调用它。因此,我们需要一个关注异常代码的包装器:
现在您可以为 badmath() 编写一个包装器,以获取调用的信号处理程序:
反过来,该包装器可以由可以从任何托管代码调用的 C++/CLI 类调用。它需要确保在调用之前启用浮点异常并在调用后再次恢复:
注意对 _control87() 的调用,它启用除“不精确结果”之外的所有浮点异常。这是允许代码被抖动所必需的。如果你不屏蔽它,那么 CLR 就会惨死,一遍又一遍地抛出异常,直到这个站点的名字结束它。希望您的信号处理程序不需要它。
There are several very serious pain points here. Enabling floating point exceptions is grossly incompatible with managed code execution. Down to the basics, you can easily crash the JIT-compiler. Which is the problem you are battling when you use _control87().
And yes, you'll get a CLR exception, it puts an exception backstop in place whenever it executes native code. A signal handler is only ever called when an exception is raised and there's no code to handle it. Inevitably the CLR sees the exception before the C runtime library can see it. So you'll never get the SIGFPE handler call.
The only decent way to have a shot at this is write a wrapper that catches the exception before the CLR can. Also very, very important that you carefully manage the FPU control word, you can only afford having FPU exceptions enabled while the native code is running. This takes a bunch of gritty code, up-front warning that you are not going to enjoy it very much.
You didn't post any snippet so I'll have to make up a silly example:
In order to get fpehandler() called, you need to call the exception handler inside the C runtime library. Luckily it is exposed and you can link it, you only need a declaration for it so you can call it:
You need to make sure that it is only ever called for floating point exceptions. So we need a wrapper that pays attention to the exception code:
Now you can write a wrapper for badmath() that gets the signal handler called:
Which in turn can be called by a C++/CLI class that you can call from any managed code. It needs to ensure that floating point exceptions are enabled before the call and restored again after the call:
Note the call to _control87(), it enables all floating exceptions except "inexact result". This is necessary to allow the code to be jitted. If you don't mask it then the CLR dies a horrible death, throwing exceptions over and over again until this site's name puts an end to it. Hopefully your signal handler doesn't need it.