Bear in mind that code inside the function called by finally must not allow exceptions to propagate out, or you end up with problems when the destructor is called while cleaning up an exception in the calling code. You might want to put a catch(...) block in the finally_type destructor for that reason.
I agree that the C++98 solution is much uglier, to the point of not being usable. C++98 is never going to be able to capture arbitrary code for execution later. I'm not so sure this is a problem though, since I expect the cleanup code to have to be specialised only per-type, not per-usage. I want the class to be responsible for cleaning itself up, not the client of the class, since the latter can lead to mistakes or redundancy. The most idiomatic solution is to put this cleanup code in the destructor.
Edit: If you have to support a non-RAII class with an arbitrary cleanup method, then you can make a scoped holder that will call the method for you. It proved more fiddly than I expected, here's the best I could come up with:
class ScopedAction { public: virtual ~ScopedAction() = 0; }; ScopedAction::~ScopedAction() { } template<class T, class Arg1, void (T::* p)(Arg1)> class SpecialisedScopedAction : public ScopedAction { public: SpecialisedScopedAction(T &target, Arg1 &arg) : target_(target), arg_(arg) { } ~SpecialisedScopedAction() { try { (target_.*p)(arg_); } catch (...) {} } private: T &target_; Arg1 &arg_; }; class ScopedActionHolder { public: ScopedActionHolder(ScopedAction * action) : action_(action) {} ~ScopedActionHolder() { delete action_; } private: ScopedAction * action_; }; template<class T, class Arg1, void (T::* fn)(Arg1)> ScopedAction * makeScopedAction(T & t, Arg1 & arg) { return new SpecialisedScopedAction<T, Arg1, fn>(t, arg); }
Using the following non-RAII class:
enum TransactionState { COMMIT, ROLLBACK }; class DBConnection { public: void finish(TransactionState state) { if (state == COMMIT) { cout << "committing transaction" << endl; } else { cout << "rolling back transaction" << endl; } } };
You can make a scoped object that calls the finish method, binding in a parameter from local scope like this:
void test() { DBConnection conn; TransactionState tstate = ROLLBACK; ScopedActionHolder cleanup(makeScopedAction<DBConnection, TransactionState, &DBConnection::finish>(conn, tstate)); cout << "Doing something" << endl; tstate = COMMIT; }
Having to specify the types as template arguments even though they're implicit in the member function pointer type is annoying, I was hoping to work around this but I've run out of time. Also, the syntax is non-obvious and it'll need a new overload of the makeScopedAction function for every different number of method parameters that needs to be supported.
Come to think about it, std::functional or boost::lambda may both be of some help here.