编码
#include <csetjmp>
template <typename Callable>
void create_checkpoint(std::jmp_buf buf, Callable&& callable)
{
if (setjmp(buf) != 0)
{
callable();
}
}
#include <iostream>
struct announcer {
int id;
announcer(int id):
id{id}
{
std::cout << "created announcer with id " << id << '\n';
}
~announcer() {
std::cout << "destructing announcer with id " << id << '\n';
}
};
void oopsie(std::jmp_buf buf, bool shouldJump)
{
if (shouldJump)
{
// std::cout << "performing jump...\n";
std::longjmp(buf, 1);
}
}
void test1()
{
std::jmp_buf buf;
announcer a1{1};
create_checkpoint(buf, []() {throw std::exception();});
oopsie(buf, true);
}
void test2()
{
std::jmp_buf buf;
announcer a1{1};
create_checkpoint(buf, []() {throw std::exception();});
oopsie(buf, false);
announcer a2{2};
create_checkpoint(buf, []() {throw std::exception();});
oopsie(buf, true);
}
int main()
{
try
{
test1();
}
catch (...)
{}
try
{
test2();
}
catch (...)
{}
}
语境
我必须调用一些通过
longjmp
报告错误的C库。为了提供强大的异常保证,我想创建一个与std::lock_guard
非常相似的函数,例如我只是编写create_checkpoint(buf, handler)
并继续调用C库函数,直到分配更多资源为止(如果我理解正确,则不会调用setjmp
行下方创建的对象的析构函数)。题
为什么在这种情况下会调用未定义的行为,我该如何解决?
我如何发现这是未定义的行为?
std::cout
和Printing message to std::longjmp
之前的not printing产生非常不同的结果,即使该行与控制流无关。我现在了解什么?
我知道
std::longjmp
本质上是还原寄存器并跳转到setjmp
宏保存的指令指针。另外,functions are not optimized away,至少在编译期间,有一条指令调用longjmp
。将
create_checkpoint
转换为宏seems to solve the issue。但是我不知道有更好的方法吗? 最佳答案
从https://en.cppreference.com/w/cpp/utility/program/longjmp
由于您未遵循此规则,因此您的程序具有未定义的行为