/* Copyright (C) 2019-2023 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef COMMON_SCOPE_EXIT_H #define COMMON_SCOPE_EXIT_H #include <functional> #include <type_traits> #include "gdbsupport/preprocessor.h" /* scope_exit is a general-purpose scope guard that calls its exit function at the end of the current scope. A scope_exit may be canceled by calling the "release" method. The API is modeled on P0052R5 - Generic Scope Guard and RAII Wrapper for the Standard Library, which is itself based on Andrej Alexandrescu's ScopeGuard/SCOPE_EXIT. There are two forms available: - The "make_scope_exit" form allows canceling the scope guard. Use it like this: auto cleanup = make_scope_exit ( <function, function object, lambda> ); ... cleanup.release (); // cancel - If you don't need to cancel the guard, you can use the SCOPE_EXIT macro, like this: SCOPE_EXIT { // any code you like here. } See also forward_scope_exit. */ /* CRTP base class for cancelable scope_exit-like classes. Implements the common call-custom-function-from-dtor functionality. Classes that inherit this implement the on_exit() method, which is called from scope_exit_base's dtor. */ template <typename CRTP> class scope_exit_base { public: scope_exit_base () = default; ~scope_exit_base () { if (!m_released) { auto *self = static_cast<CRTP *> (this); self->on_exit (); } } /* This is needed for make_scope_exit because copy elision isn't guaranteed until C++17. An optimizing compiler will usually skip calling this, but it must exist. */ scope_exit_base (const scope_exit_base &other) : m_released (other.m_released) { other.m_released = true; } void operator= (const scope_exit_base &) = delete; /* If this is called, then the wrapped function will not be called on destruction. */ void release () noexcept { m_released = true; } private: /* True if released. Mutable because of the copy ctor hack above. */ mutable bool m_released = false; }; /* The scope_exit class. */ template<typename EF> class scope_exit : public scope_exit_base<scope_exit<EF>> { /* For access to on_exit(). */ friend scope_exit_base<scope_exit<EF>>; public: template<typename EFP, typename = gdb::Requires<std::is_constructible<EF, EFP>>> scope_exit (EFP &&f) try : m_exit_function ((!std::is_lvalue_reference<EFP>::value && std::is_nothrow_constructible<EF, EFP>::value) ? std::move (f) : f) { } catch (...) { /* "If the initialization of exit_function throws an exception, calls f()." */ f (); } template<typename EFP, typename = gdb::Requires<std::is_constructible<EF, EFP>>> scope_exit (scope_exit &&rhs) noexcept (std::is_nothrow_move_constructible<EF>::value || std::is_nothrow_copy_constructible<EF>::value) : m_exit_function (std::is_nothrow_constructible<EFP>::value ? std::move (rhs) : rhs) { rhs.release (); } /* This is needed for make_scope_exit because copy elision isn't guaranteed until C++17. An optimizing compiler will usually skip calling this, but it must exist. */ scope_exit (const scope_exit &other) : scope_exit_base<scope_exit<EF>> (other), m_exit_function (other.m_exit_function) { } void operator= (const scope_exit &) = delete; void operator= (scope_exit &&) = delete; private: void on_exit () { m_exit_function (); } /* The function to call on scope exit. */ EF m_exit_function; }; template <typename EF> scope_exit<typename std::decay<EF>::type> make_scope_exit (EF &&f) { return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (f)); } namespace detail { enum class scope_exit_lhs {}; template<typename EF> scope_exit<typename std::decay<EF>::type> operator+ (scope_exit_lhs, EF &&rhs) { return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (rhs)); } } /* Register a block of code to run on scope exit. Note that the local context is captured by reference, which means you should be careful to avoid inadvertently changing a captured local's value before the scope exit runs. */ #define SCOPE_EXIT \ auto CONCAT(scope_exit_, __LINE__) = ::detail::scope_exit_lhs () + [&] () #endif /* COMMON_SCOPE_EXIT_H */