From 5c921a21c409106e297b314289b18497bf32f1f1 Mon Sep 17 00:00:00 2001 From: florijan <florijan@memgraph.io> Date: Fri, 11 Aug 2017 09:32:46 +0200 Subject: [PATCH] utils::auto_scope refactor Summary: Changed on-scope-exit-mechanism from macro (with two auto-generated variables and an all-capturing lambda) to an explicitly created variable that takes an std::function argument. Reviewers: buda, mislav.bradac, teon.banek Reviewed By: buda Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D659 --- poc/memory/block_allocator.hpp | 5 ++- src/utils/auto_scope.hpp | 63 ------------------------------ src/utils/on_scope_exit.hpp | 34 ++++++++++++++++ src/utils/stacktrace.hpp | 4 +- src/utils/terminate_handler.hpp | 1 - tests/unit/utils_on_scope_exit.cpp | 15 +++++++ 6 files changed, 54 insertions(+), 68 deletions(-) delete mode 100644 src/utils/auto_scope.hpp create mode 100644 src/utils/on_scope_exit.hpp create mode 100644 tests/unit/utils_on_scope_exit.cpp diff --git a/poc/memory/block_allocator.hpp b/poc/memory/block_allocator.hpp index cdb7c41d1..7eb7452d5 100644 --- a/poc/memory/block_allocator.hpp +++ b/poc/memory/block_allocator.hpp @@ -3,7 +3,7 @@ #include <memory> #include <vector> -#include "utils/auto_scope.hpp" +#include "utils/on_scope_exit.hpp" /* @brief Allocates blocks of block_size and stores * the pointers on allocated blocks inside a vector. @@ -41,7 +41,8 @@ class BlockAllocator { if (unused_.size() == 0) unused_.emplace_back(); auto ptr = unused_.back().data; - Auto(unused_.pop_back()); + // TODO is it necessary to use OnScopeExit here? ptr is copied by value, right? + utils::OnScopeExit on_exit([this]() { unused_.pop_back(); }); return ptr; } diff --git a/src/utils/auto_scope.hpp b/src/utils/auto_scope.hpp deleted file mode 100644 index 7fdecc8be..000000000 --- a/src/utils/auto_scope.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include <utility> - -/** - * @brief Calls a cleanup function on scope exit - * - * consider this example: - * - * void hard_worker() - * { - * resource.enable(); - * do_stuff(); // throws exception - * resource.disable(); - * } - * - * 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 - * - * void hard_worker() - * { - * resource.enable(); - * Auto(resource.disable()); - * do_stuff(); // throws exception - * } - * - * now, resource.disable will be called every time it goes out of scope - * regardless of the exception - * - * @tparam F Lambda which holds a wrapper function around the cleanup code - */ -template <class F> -class OnScopeExit { - public: - OnScopeExit(F& f) : f(f) {} - - ~OnScopeExit() { - // calls the cleanup function - f(); - } - - private: - F& f; -}; - -#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)); - -#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 diff --git a/src/utils/on_scope_exit.hpp b/src/utils/on_scope_exit.hpp new file mode 100644 index 000000000..9b63e7f86 --- /dev/null +++ b/src/utils/on_scope_exit.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include <functional> + +namespace utils { + +/** + * Calls a function in it's destructor (on scope exit). + * + * Example usage: + * + * void long_function() { + * resource.enable(); + * // long block of code, might throw an exception + * resource.disable(); // we want this to happen for sure, and function end + * } + * + * Can be nicer and safer: + * + * void long_function() { + * resource.enable(); + * OnScopeExit on_exit([&resource] { resource.disable(); }); + * // long block of code, might trow an exception + * } + */ +class OnScopeExit { + public: + OnScopeExit(const std::function<void()> function) : function_(function) {} + ~OnScopeExit() { function_(); } + + private: + std::function<void()> function_; +}; +} diff --git a/src/utils/stacktrace.hpp b/src/utils/stacktrace.hpp index a29e85af6..20556e402 100644 --- a/src/utils/stacktrace.hpp +++ b/src/utils/stacktrace.hpp @@ -5,7 +5,7 @@ #include <fmt/format.h> #include <stdexcept> -#include "utils/auto_scope.hpp" +#include "utils/on_scope_exit.hpp" class Stacktrace { public: @@ -28,7 +28,7 @@ class Stacktrace { // will this leak if backtrace_symbols throws? char **symbols = nullptr; - Auto(free(symbols)); + utils::OnScopeExit on_exit([&symbols]() { free(symbols); }); symbols = backtrace_symbols(addresses, depth); diff --git a/src/utils/terminate_handler.hpp b/src/utils/terminate_handler.hpp index 9b6a7ceef..e818e61dd 100644 --- a/src/utils/terminate_handler.hpp +++ b/src/utils/terminate_handler.hpp @@ -1,6 +1,5 @@ #pragma once -#include "utils/auto_scope.hpp" #include "utils/stacktrace.hpp" #include <execinfo.h> diff --git a/tests/unit/utils_on_scope_exit.cpp b/tests/unit/utils_on_scope_exit.cpp new file mode 100644 index 000000000..b41ef85d6 --- /dev/null +++ b/tests/unit/utils_on_scope_exit.cpp @@ -0,0 +1,15 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "utils/on_scope_exit.hpp" + + +TEST(OnScopeExit, BasicUsage) { + int variable = 1; + { + ASSERT_EQ(variable, 1); + utils::OnScopeExit on_exit([&variable] { variable = 2; }); + EXPECT_EQ(variable, 1); + } + EXPECT_EQ(variable, 2); +}