如何在堆栈展开时调用函数

How can I call a function on stack unwind?

本文关键字:调用 函数 堆栈      更新时间:2023-10-16

我想写的是

void foo()
{
    int a = 5;
    ExecuteOnUnwind eou(bind(&cleanupFunc, a));
}

使得当函数返回或抛出异常时调用cleanupFunc(a)。有没有现成的设施可以帮我做这件事?我没能在谷歌上找到合适的短语,但似乎有一些东西可以做到这一点。如果没有,我就在下面快速整理出一个解决方案。奇怪的是,它似乎在发布模式下不起作用,但在vc10的调试中工作-我如何调整实现以使其在不冒额外调用临时的风险的情况下始终工作?

编辑:修复涉及使用shared_ptr;也减轻了对暂时破坏的担忧。新代码在

下面
template <typename T>
struct ExecuteOnUnwindHelper
{   
    ExecuteOnUnwindHelper(const T & _functor) : mFunctor(_functor)
    {
    }
    ~ExecuteOnUnwindHelper()
    {
        mFunctor();
    }
    const T & mFunctor;
};
template <typename T>
boost::shared_ptr<ExecuteOnUnwindHelper<T>> ExecuteOnUnwind(const T & _functor)
{
    return boost::shared_ptr<ExecuteOnUnwindHelper<T>>(new ExecuteOnUnwindHelper<T>(_functor));
}
void cleanupFunc(int a)
{
    wcout << L"cleanup" << endl;
}
void foo()
{
    int a = 5;
    auto eou = ExecuteOnUnwind(boost::bind(&cleanupFunc, 5));
}
int main()
{
    foo();
    return 0;
}

编译器必须以某种方式优化堆栈上变量的创建,因为它认为它没有被使用。也许它只是内联了对函数的调用,跳过了创建/销毁部分(我想说这是最有可能的)。它认为全局语义是保留的,但它实际上不是一个安全的优化,如你的例子所示。

我认为这是一个错误的优化,因为它显然改变了高级语义。用不同的编译器进行测试会很有趣。当我在家里有机会的时候,我会尝试VS2012。

无论如何,要强制它通过创建/销毁序列,只需使用boost::shared_ptr,它将负责创建对象并在对象超出作用域时销毁它,无论是通过return语句还是通过异常抛出。

最简单的解决方案是将所需的功能放入类型的析构函数中。在您的情况下,不需要自动/智能指针,堆栈将是足够的,并且可能会消除您可能遇到的编译器问题。

class ExecuteOnUnwind
{
public:
~ExecuteOnUnwind()
{
/** Do something */
}
int data;
};
void foo()
{
  ExecuteOnUnwind runOnExit;
/** Functions code here */
runOnExit.data = 5;
}

如果这不起作用,那么你可以禁用受影响代码的优化(即析构函数):

#pragma optimize( "", off )
...
#pragma optimize( "", on )