在C回调中在C ++中抛出异常,可能跨越动态库边界......是否安全?

时间:2012-06-08 21:08:42

标签: c++ c libjpeg

我现在正在使用libjpeg来保存JPEG图像。如果出现错误,libjpeg的默认行为是调用exit(),我想避免使用它,因为它不是我程序的致命错误。 libjpeg allows you to use your own error manager,并强制要求您使用自己的error_exit()函数(默认情况下调用exit()),必须才能将控制权返回给调用者。 libjpeg建议使用setjmp.h来满足此要求,而不是exit()该程序。

但是,我正在编写一个C ++程序,我可以访问异常。 This question's answer声明从回调中抛出异常是安全的(如明确定义的行为)。但它没有提到动态库,并且通常的经验法则是不要在动态库边界之间抛出异常。

以下是一个例子:

#include <iostream>
#include <jpeglib.h>
#include <cstdio>
#include <stdexcept>

static void handleLibJpegFatalError(j_common_ptr cinfo)
{
  (*cinfo->err->output_message)(cinfo);
  throw std::runtime_error("error in libjpeg, check stderr");
}

int main()
{
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;
  FILE* file = std::fopen("out.jpeg", "wb"); // assume this doesn't fail for this example

  try
    {
      cinfo.err = jpeg_std_error(&jerr);
      jerr.error_exit = handleLibJpegFatalError;

      // let's say this triggers a fatal error in libjpeg and handleLibJpegFatalError() is called
      // by libjpeg
      jpeg_create_compress(&cinfo);
    }
  catch (...)
    {
      std::cerr << "Error saving the JPEG!\n";
    }

  jpeg_destroy_compress(&cinfo);
  std::fclose(file);
}

我想知道的是:我可以从这个回调中抛出异常,并在我的应用程序中捕获它,即使libjpeg被编译为动态库吗? libjpeg可能是静态的或动态库,如果它是动态库,它可能使用不同的编译器构建。但是,抛出并捕获异常的代码肯定会在同一个编译单元中。 以上代码是否安全?

仅供参考,我正在为OS X和Windows开发(并考虑到Linux可能性的未来),所以我更感兴趣的是,如果知道这是一般定义良好的行为,而不是特定平台/编译器。

3 个答案:

答案 0 :(得分:4)

另一个答案适用于此。展开堆栈时,什么都不会被破坏。如果库在内部使用一些疯狂的调用约定,那么它甚至无关紧要,只要它不会特别混淆你的C ++实现的异常处理结构(它不会作为C程序)。我所知道的没有C ++实现通过弹出堆栈帧来找到catch块(这会使优化成为一场噩梦),它们都维护着异常处理的内部结构。只要调用链中较低的调用不会破坏这些结构,堆栈展开将对所有个人代码完全正常工作。现在,一般情况下,这很可能会使库中的内部状态混乱,因为您永远不会将执行返回到库中进行清理,但是在您的错误回调的情况下,libjpeg希望控制流不返回并具有大概已经清理过了。

在这种情况下,我会去做。一般来说,我只会从C回调中抛出致命的异常。

希望有所帮助。

答案 1 :(得分:4)

这不安全。根据相关的非C ++库代码的编译方式,可能不存在必要的展开表。这只是实际的原因,它可能会失败;概念上的原因是它只是未定义的行为。

您应该按照文档操作并使用setjmp / longjmp来调用libjpeg代码,然后如果要使用异常,则立即在if (setjmp(...)) { ... }正文中抛出异常

答案 2 :(得分:0)

正如在其他答案中所讨论的那样,只要您控制所有模块,并且它们将由相同的工具链(包括静态链接)构建,它应该是安全的。

但是,我想在此some toolchains require this support to be turned on添加一个警告,因为libjpeg的功能已标记为extern "C"。默认情况下,Visual Studio假定此类函数不会传播异常。

如果您不打开此开关,请多加痛苦。在意识到这一点之前,我花了几个小时在一个几乎与您相同的测试用例上。 ?