16

我现在正在使用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);
}

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

仅供参考,我正在为 OS X 和 Windows 开发(并牢记 Linux 的未来可能性),所以我更感兴趣的是,这是否被认为是一般定义明确的行为,而不是针对特定平台/编译器。

4

3 回答 3

6

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

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

希望有帮助。

于 2012-06-08T22:42:14.757 回答
5

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

您应该遵循文档并使用setjmp/来获取对 libjpeg 代码的调用之外,然后如果您想使用异常longjmp,则立即在正文中抛出异常。if (setjmp(...)) { ... }

于 2012-06-08T23:06:10.970 回答
0

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

我想在这里添加一个警告,一些工具链需要打开此支持,因为 libjpeg 的功能已标记extern "C"。默认情况下,Visual Studio 假定此类函数不会传播异常。

如果你不打开它,预计会很痛苦。在我意识到这一点之前,我花了几个小时在一个几乎与你相同的测试用例上。

于 2019-11-22T17:29:38.497 回答