【问题标题】:Portably handle exceptional errors in C++可移植地处理 C++ 中的异常错误
【发布时间】:2008-09-02 10:57:10
【问题描述】:

我正在将一个 Visual C++ 应用程序移植到 GCC(应该在 MingW 和 Linux 上构建)。

现有代码在一些地方使用__try { ... } __except(1) { ... } 块,因此几乎没有什么(可能是内存不足类型错误?)会使程序退出而无需进行一些最少的日志记录。

用 GCC 做类似的事情有哪些选择?

编辑:感谢 Visual Studio 中指向 /EH 选项的指针,我现在需要一些关于如何在 Linux 上处理信号的示例。我找到了 2002 年的 this message

除了SIGFPESIGSEVG 之外,我还应该注意哪些其他信号? (主要关心那些可能因 me 做错事而引发的问题)

赏金信息: 我希望我的应用程序能够在退出之前自行记录尽可能多的错误情况。

我可能会收到哪些信号,哪些信号通常不可能在之后记录错误消息? (内存不足,还有什么?)

如何以一种可移植的方式处理异常和(最重要的)信号,以使代码至少在 Linux 和 MingW 上运行相同。 #ifdef 没问题。

我不只是有一个记录失败的包装进程的原因是,出于性能原因,我保存将一些数据写入磁盘直到最后一分钟,所以如果出现问题,我想尽一切可能尝试编写退出前输出数据。

【问题讨论】:

    标签: exception visual-c++ gcc cross-platform portability


    【解决方案1】:

    try { xxx } catch(...) { xxx } 会更便携,但可能不会捕获那么多。这取决于编译器设置和环境。

    使用默认的 VC++ 设置,异步 (SEH) 错误不会传递到 C++ EH 基础架构;要捕获它们,您需要改用 SEH 处理程序 (__try/__except)。 VC++ 允许您通过 C++ 错误处理来路由 SEH 错误,这允许 catch(...) 捕获 SEH 错误;这包括内存错误,例如空指针取消引用。 Details.

    然而,在 Linux 上,Windows 使用 SEH 处理的许多错误都是通过信号指示的。这些永远不会被 try/catch 捕获;要处理它们,您需要一个信号处理程序。

    【讨论】:

      【解决方案2】:

      为什么不使用 C++ 标准异常来代替 MSFT 的专有扩展? C++ 有一个异常处理的概念。

      struct my_exception_type : public logic_error {
          my_exception_type(char const* msg) : logic_error(msg) { }
      };
      
      try {
          throw my_exception_type("An error occurred");
      } catch (my_exception_type& ex) {
          cerr << ex.what << endl;
      }
      

      C++ 也有一个“catchall”子句,所以如果你想记录异常,你可以使用下面的包装器:

      try {
          // …
      }
      catch (...) {
      }
      

      然而,这在 C++ 中效率不高,因为创建这样一个通用包装器意味着编译器必须将处理代码插入到每个后续堆栈帧中(与 .NET 等托管系统不同,只要没有实际抛出异常,异常处理就不会产生额外成本)。

      【讨论】:

      • OP 正在寻找解决比 C++ 语言级别异常低级别异常条件的方法。在 POSIX 世界中,这意味着为(大多数)所有东西安装信号处理程序。在 MSVC 中,__try/__catch 机制处理类似的错误(包括 SEGFAULT 的 Win32 等效项)
      • 正确的陷阱。但是您可以配置 MSVC 以使用标准 try/catch 捕获 SEH,如 DrPizza 所述。
      【解决方案3】:

      为了可移植性,要尝试的一件事是对大多数普通异常使用 try-catch 块,然后设置终止处理程序 (set_terminate_handler) 以具有可用于灾难性退出条件的最小挂钩。您还可以尝试添加类似 atexit 或 on_exit 处理程序的内容。当然,当您输入这些函数时,您的执行环境可能会很奇怪或损坏,因此请注意您认为环境正常的程度。

      最后,当使用常规的 try-catch 对时,您可以考虑使用函数 try 块,而不是在函数体中打开 try 块:

      int foo(int x) try {
        // body of foo
      } catch (...) {
         // be careful what's done here!
      }
      

      它们是相对未知的 C++ 块,在某些情况下,即使在部分(小规模)堆栈损坏的情况下也可能提供恢复。

      最后,是的,您可能想要调查哪些信号您可以自己持续处理或您可能会中止哪些信号,如果您想要更少的处理机制,您可以考虑调用非抛出版本new 运算符,并在需要时进行编译以不生成浮点异常(您始终可以检查 isnan(.)、isfinite(.)、FP 结果以保护自己)。

      关于最后一点,请注意:我注意到浮点结果分类函数可以在 linux 和 windows 下的不同标头中...因此您可能必须对这些包含进行条件化。

      如果你觉得很顽皮,就用 setjmp 和 longjmp 来写吧(开个玩笑……)。

      【讨论】:

        【解决方案4】:

        使用catch(...) 捕获 C++ 异常已经让你陷入了困境。

        试图捕捉catch(...) 没有捕捉到的错误会让你陷入未定义的行为。没有 C++ 代码可以保证工作。您的最小日志代码可能会导致导弹发射。

        我的建议是不要尝试catch(...)。仅捕获您可以有意义且安全地记录的异常,并让操作系统处理其余的(如果有)。

        如果在根本原因之上还有错误处理代码故障,事后调试会变得很糟糕。

        【讨论】:

          【解决方案5】:

          一种易于使用、可移植且几乎不使用任何资源的方法是捕获空类。我知道这听起来可能很奇怪,但它可能非常有用。

          这是我为另一个问题制作的示例,也适用于您的问题:link

          另外,你可以有不止 1 个捕获:

          try
          {
             /* code that may throw exceptions */
          }
          catch (Error1 e1)
          {
             /* code if Error1 is thrown */
          }
          catch (Error2 e2)
          {
             /* code if Error2 is thrown */
          }
          catch (...)
          {
             /* any exception that was not expected will be caught here */
          }
          

          【讨论】:

            猜你喜欢
            • 2014-01-08
            • 2017-10-11
            • 2011-07-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-03-09
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多