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:
- it's not possible to break from an outer loop with the
break
statement from within thetry
andcatch()
's blocks, since they live within a lambda function; - there must be at least one
catch()
block after thetry
: it's a C++ requirement; - if the function has a return value other than void but there's no return within the
try
andcatch()'s
blocks, compilation will fail because thefinally
macro will expand to code that will want to return avoid
. This could be, err, avoided by having afinally_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. :)