memgraph/src/utils/auto_scope.hpp

64 lines
1.6 KiB
C++
Raw Normal View History

#pragma once
2015-10-09 07:24:12 +08:00
#include <utility>
/**
* @brief Calls a cleanup function on scope exit
2015-10-09 07:24:12 +08:00
*
* consider this example:
2015-10-09 07:24:12 +08:00
*
* void hard_worker()
* {
* resource.enable();
* do_stuff(); // throws exception
* resource.disable();
* }
2015-10-09 07:24:12 +08:00
*
* if do_stuff throws an exception, resource.disable is never called
* and the app is left in an inconsistent state. ideally, you would like
* to call resource.disable regardles of the exception being thrown.
* OnScopeExit makes this possible and very convenient via a 'Auto' macro
2015-12-08 04:50:07 +08:00
*
* void hard_worker()
* {
* resource.enable();
* Auto(resource.disable());
* do_stuff(); // throws exception
* }
2015-10-09 07:24:12 +08:00
*
* now, resource.disable will be called every time it goes out of scope
* regardless of the exception
2015-10-09 07:24:12 +08:00
*
* @tparam F Lambda which holds a wrapper function around the cleanup code
2015-10-09 07:24:12 +08:00
*/
template <class F>
class OnScopeExit {
public:
OnScopeExit(F& f) : f(f) {}
2015-10-09 07:24:12 +08:00
~OnScopeExit() {
// calls the cleanup function
f();
}
2015-10-09 07:24:12 +08:00
private:
F& f;
2015-10-09 07:24:12 +08:00
};
#define TOKEN_PASTEx(x, y) x##y
#define TOKEN_PASTE(x, y) TOKEN_PASTEx(x, y)
#define Auto_INTERNAL(Destructor, counter) \
auto TOKEN_PASTE(auto_func_, counter) = [&]() { Destructor; }; \
OnScopeExit<decltype(TOKEN_PASTE(auto_func_, counter))> TOKEN_PASTE( \
auto_, counter)(TOKEN_PASTE(auto_func_, counter));
2015-10-09 07:24:12 +08:00
#define Auto(Destructor) Auto_INTERNAL(Destructor, __COUNTER__)
// -- example:
// Auto(f());
// -- is expended to:
// auto auto_func_1 = [&]() { f(); };
// OnScopeExit<decltype(auto_func_1)> auto_1(auto_func_1);
// -- f() is called at the end of a scope