一个小的 RAII 提交/回滚范围保护可能如下所示:
#include <utility>
#include <functional>
class CommitOrRollback
{
bool committed;
std::function<void()> rollback;
public:
CommitOrRollback(std::function<void()> &&fail_handler)
: committed(false),
rollback(std::move(fail_handler))
{
}
void commit() noexcept { committed = true; }
~CommitOrRollback()
{
if (!committed)
rollback();
}
};
所以,我们假设我们总是在事务成功后创建保护对象,并且commit
只有在所有事务都成功后才调用。
void complicated_task_a();
void complicated_task_b();
void rollback_a();
void rollback_b();
int main()
{
try {
complicated_task_a();
// if this ^ throws, assume there is nothing to roll back
// ie, complicated_task_a is internally exception safe
CommitOrRollback taskA(rollback_a);
complicated_task_b();
// if this ^ throws however, taskA will be destroyed and the
// destructor will invoke rollback_a
CommitOrRollback taskB(rollback_b);
// now we're done with everything that could throw, commit all
taskA.commit();
taskB.commit();
// when taskA and taskB go out of scope now, they won't roll back
return 0;
} catch(...) {
return 1;
}
}
PS。正如Anon Mail所说,如果你有很多 taskX 对象,最好将所有这些taskX对象推送到一个容器中,从而为容器提供相同的语义(在容器上调用 commit 以使其提交每个拥有的保护对象)。
聚苯乙烯。原则上,您可以std::uncaught_exception
在 RAII dtor 中使用,而不是显式提交。我更喜欢在此处显式提交,因为我认为它更清晰,并且如果您提前退出范围并使用 areturn FAILURE_CODE
而不是异常也可以正常工作。