I came up with a finally
macro that can be used almost like¹ the finally
keyword in Java; it makes use of std::exception_ptr
and friends, lambda functions and std::promise
, so it requires C++11
or above; it also makes use of the compound statement expression GCC extension, which is also supported by clang.
WARNING: an earlier version of this answer used a different implementation of the concept with many more limitations.
First, let's define a helper class.
#include <future>
template <typename Fun>
class FinallyHelper {
template <typename T> struct TypeWrapper {};
using Return = typename std::result_of<Fun()>::type;
public:
FinallyHelper(Fun body) {
try {
execute(TypeWrapper<Return>(), body);
}
catch(...) {
m_promise.set_exception(std::current_exception());
}
}
Return get() {
return m_promise.get_future().get();
}
private:
template <typename T>
void execute(T, Fun body) {
m_promise.set_value(body());
}
void execute(TypeWrapper<void>, Fun body) {
body();
}
std::promise<Return> m_promise;
};
template <typename Fun>
FinallyHelper<Fun> make_finally_helper(Fun body) {
return FinallyHelper<Fun>(body);
}
Then there's the actual macro.
#define try_with_finally for(auto __finally_helper = make_finally_helper([&] { try
#define finally }); \
true; \
({return __finally_helper.get();})) \
/***/
It can be used like this:
void test() {
try_with_finally {
raise_exception();
}
catch(const my_exception1&) {
/*...*/
}
catch(const my_exception2&) {
/*...*/
}
finally {
clean_it_all_up();
}
}
The use of std::promise
makes it very easy to implement, but it probably also introduces quite a bit of unneeded overhead which could be avoided by reimplementing only the needed functionalities from std::promise
.
¹ CAVEAT: there are a few things that don't work quite like the java version of finally
. Off the top of my head:
break
statement from within the try
and catch()
's blocks, since they live within a lambda function;catch()
block after the try
: it's a C++ requirement; try
and catch()'s
blocks, compilation will fail because the finally
macro will expand to code that will want to return a void
. This could be, err, avoided by having a finally_noreturn
macro of sorts.All in all, I don't know if I'd ever use this stuff myself, but it was fun playing with it. :)