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

Cor*_*lks 15 c c++ libjpeg

我现在正在使用libjpeg来保存JPEG图像.如果有错误,libjpeg的默认行为是调用exit(),我想避免,因为它不是我的程序的致命错误.的libjpeg 可以让你使用你自己的错误管理器,并授权,如果您使用自己的error_exit()函数(调用exit()默认情况下),你必须不控制权返回给调用者.libjpeg建议使用setjmp.h来满足此要求而不是exit()程序.

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

这是一个例子:

#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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

nw.*_*nw. 6

另一个答案适用于此。展开堆栈时不会丢弃任何内容。库是否在内部使用一些疯狂的调用约定也没有关系,只要它不会特别干扰您的 C++ 实现的异常处理结构(它不会作为 C 程序)。我所知道的 C++ 实现没有通过弹出堆栈帧来找到 catch 块(这会使优化成为一场噩梦),它们都维护用于异常处理的内部结构。只要调用链中较低的调用不会与这些结构混淆,堆栈展开将非常适合您的所有个人代码。现在,一般来说,这很可能会使库的内部状态混乱,因为您永远不会将执行返回到库进行清理,

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

希望有所帮助。


R..*_*R.. 5

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

您应该遵循文档并使用setjmp/ longjmp来获取对libjpeg代码的调用,然后if (setjmp(...)) { ... }如果要使用异常,则立即在主体中引发异常。