Функционально эквивалентный вариант, лишенный недостатков, указанных в разделе Solution 1: Brute Force
ScopeGuard:
The code works, but at the cost of increased size and clumsiness. The two-liner just became a ten-liner. This technique isn't appealing; imagine littering all of your code with such try-catch statements.
Moreover, this technique doesn't scale well. Imagine you have a third operation to do. In that case, things suddenly become much clumsier. You can choose between equally awkward solutions: nested try statements or a more complicated control flow featuring additional flags. These solutions raise code bloating issues, efficiency issues, and, most important, severe understandability and maintenance issues.
class Method
{
public:
virtual void operator()() const = 0;
};
class MyMethod : public Method
{
public:
MyMethod(/*params*/);
virtual void operator()() const { /*does real actions*/ }
private:
/*fields for params*/
}
void exec(QSqlDatabase& db, const Method& method)
{
db.transaction();
try {
method(); // throws at error
sqlDatabase->commit();
}
catch (...) {
sqlDatabase->rollback();
throw;
}
}
// Использование:
...
QSqlDataBase db(...);
MyMethod method(/*params*/);
exec(db, method);
...
Указанный вариант соотносится со ScopeGuard-вариантом примерно также, как std::for_each c непосредственным циклом. Вообщем, вопрос предпочтений.