我现在正在使用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可能性的未来),所以我更感兴趣的是,如果知道这是一般定义良好的行为,而不是特定平台/编译器。
答案 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假定此类函数不会传播异常。
如果您不打开此开关,请多加痛苦。在意识到这一点之前,我花了几个小时在一个几乎与您相同的测试用例上。 ?