summaryrefslogtreecommitdiff
path: root/gdbsupport/scope-exit.h
diff options
context:
space:
mode:
Diffstat (limited to 'gdbsupport/scope-exit.h')
-rw-r--r--gdbsupport/scope-exit.h186
1 files changed, 186 insertions, 0 deletions
diff --git a/gdbsupport/scope-exit.h b/gdbsupport/scope-exit.h
new file mode 100644
index 00000000000..192f6665fcf
--- /dev/null
+++ b/gdbsupport/scope-exit.h
@@ -0,0 +1,186 @@
+/* Copyright (C) 2019-2020 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 */