我正在开发一个C ++应用程序,它使用另一个团队用C编写的库。库的编写者喜欢在发生错误时调用exit()
,这会立即结束程序,而不会在C ++应用程序中调用堆栈上对象的析构函数。应用程序设置了一些系统资源,这些资源在进程结束后不会被操作系统自动回收(共享内存区域,进程间互斥等),因此这是一个问题。
我有应用程序和库的完整源代码,但是库已经非常完善并且没有单元测试,因此更改它将是一个大问题。有没有办法“挂钩”exit()
的调用,以便我可以为我的应用程序实现正常关闭?
我正在考虑的一种可能性是创建一个 应用程序的大类 - 意味着所有清理都会在其析构函数或其成员之一的析构函数中发生 - 然后分配其中一个main()
中堆上的大对象,设置指向它的全局指针,并使用atexit()
注册一个只通过全局指针删除对象的处理程序。那可能有用吗?
有没有一种已知的好方法可以解决这个问题?
答案 0 :(得分:14)
在最糟糕的情况下,您始终可以编写自己的exit
实现并将其链接,而不是系统自己的实现。您可以在那里处理错误,也可以选择自己拨打_exit(2)
。
由于您拥有库源,因此更容易 - 只需在构建时添加-Dexit=myExit
标志,然后提供myExit
的实现。
答案 1 :(得分:4)
使用atexit安装退出处理程序并实现所需的行为
答案 2 :(得分:2)
如果你想让C库更适用于C ++,你可以在一个单独的进程中运行它。然后确保(使用退出处理程序或其他方式)当它退出时,您的主应用程序进程会注意到并抛出异常以展开其自己的堆栈。也许在某些情况下,它可以以非致命的方式处理错误。
当然,将库使用转移到另一个过程可能并不容易或特别有效。你需要做一些工作来包装界面,并通过你选择的IPC机制复制输入和输出。
但是,作为使用主进程库的解决方法,我认为您描述的库应该可以正常工作。风险在于您无法识别和隔离需要清理的所有内容,或者将来某人修改您的应用程序(或您使用的其他组件),假设堆栈将正常解开。
您可以修改库源以调用运行时或编译时可配置的函数,而不是调用exit()
。然后使用异常处理编译库并在C ++中实现该函数以引发异常。问题在于库本身可能在出错时泄漏资源,因此您必须使用该仅异常来展开堆栈(并且可能会进行一些错误报告)。即使你的应用程序涉及错误可能是非致命的,也不要抓住并继续。
答案 3 :(得分:1)
如果来电exit
而不是assert
或abort
,则有几点可以再次获得控制权:
exit
时,仍然会执行具有静态生命周期的对象的析构函数(本质上是:用static
声明的全局变量和对象)。这意味着您可以设置(少数)全局“资源管理器”对象并在其析构函数中释放资源。atexit
注册挂钩。这不仅限于一个。你可以注册更多。如果所有其他方法都失败了,因为你有了库的来源,你可以玩一些宏技巧来有效地用你自己的函数替换对exit
的调用,例如,抛出异常。