以下代码:
main.cpp :
# include <iostream>
# include <csetjmp>
# include <stdexcept>
using namespace std ;
void do_work(jmp_buf context)
{
try
{
throw runtime_error("Ouch !") ;
}
catch(exception & e)
{
}
longjmp(context, -1) ; //BP1
}
int main(int, char *[])
{
jmp_buf context ;
try
{
if( setjmp(context) != 0 )
{
throw runtime_error("Oops !") ; //BP2
}
do_work(context) ;
}
catch(exception & e)
{
cout << "Caught an exception saying : " << e.what() << endl ;
}
}
我试过调试它,但程序表现得很奇怪。有时我可以通过第一个断点(BP1),然后在BP2崩溃,有时控制永远不会到达BP1,就像程序陷入无限循环一样。我的调试技巧不能多说。
这段代码是我能得到的最小的代码,展示了MinGW 4.5的奇怪行为。我也注意到了:
do_work
函数调用,程序运行正常。try{ ... } catch(...){ }
内的do_work
块,程序运行正常。我知道C ++代码中的setjmp/longjmp
问题,但是我不得不使用它来与一些遗留的C代码进行交互。
我的问题:
感谢您的任何建议。
请在必要时重新加注。
答案 0 :(得分:3)
Unix上的longjmp(3)手册页说:
在例程之后可能不会调用longjmp()例程 调用setjmp()例程返回
我认为它解释了您对“有时控制永远不会达到BP1 ”的担忧。 我不认为“运行良好”是可靠的判断。我宁愿期望它随机运行良好,通常会搞乱堆栈。
在将longjmp / setjmp与C ++异常混合时,应该考虑一些明确的建议,以避免崩溃和未定义的行为:
问题提到在用C ++编写的程序中处理遗留C代码。在审查其中一个Boost库时,jpeg库中有关于sjlj问题的有趣讨论。讨论很长,但这里是essence with recommended options。
答案 1 :(得分:1)
C ++仅对longjmp()
的使用提出了一个额外的限制:
如果任何自动对象被抛出的异常将控制权转移到程序中的另一个(目标)点,那么在抛出点调用longjmp(jbuf,val)将控制转移到相同的位置(目的地)点有未定义的行为。(18.7)
您似乎意识到了这一点,并且您的程序没有这样做。我投票编译器缺陷。
答案 2 :(得分:0)
longjmp
和setjmp
是c函数,使用C ++异常处理语义调用它们,对象销毁语义是未定义的行为,这意味着它是否适用于实现(您的测试显示了这一点) ,SEH堆栈展开语义破坏事物,取决于使用的类型,iirc你的工作人员使用dwarf2)