Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Pedro Alves <palves@redhat.com>
To: gdb-patches@sourceware.org
Cc: Tom Tromey <tom@tromey.com>,
	Andrew Burgess <andrew.burgess@embecosm.com>
Subject: [PATCH v3 02/17] Introduce scope_exit
Date: Wed, 23 Jan 2019 15:21:00 -0000	[thread overview]
Message-ID: <20190123152131.29893-3-palves@redhat.com> (raw)
In-Reply-To: <20190123152131.29893-1-palves@redhat.com>

This add a new template class scope_exit.  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.

The main advantage of scope_exit is avoiding writing single-use RAII
classes and its boilerplate.  Following patches will remove a few of
such classes.

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. */ }

Note: scope_exit instances do not allocate anything on the heap.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
	    Andrew Burgess  <andrew.burgess@embecosm.com>
	    Tom Tromey  <tom@tromey.com>

	* common/scope-exit.h: New file.
---
 gdb/common/scope-exit.h | 186 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 186 insertions(+)
 create mode 100644 gdb/common/scope-exit.h

diff --git a/gdb/common/scope-exit.h b/gdb/common/scope-exit.h
new file mode 100644
index 0000000000..8cdbec305a
--- /dev/null
+++ b/gdb/common/scope-exit.h
@@ -0,0 +1,186 @@
+/* Copyright (C) 2019 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 "common/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 */
-- 
2.14.4


  reply	other threads:[~2019-01-23 15:21 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-23 15:21 [PATCH v3 00/17] Remove some cleanups using scope_exit Pedro Alves
2019-01-23 15:21 ` Pedro Alves [this message]
2019-01-23 15:21 ` [PATCH v3 07/17] Remove remaining cleanup from gdb/breakpoint.c Pedro Alves
2019-01-23 15:21 ` [PATCH v3 05/17] Use SCOPE_EXIT in gdbarch-selftest.c Pedro Alves
2019-01-23 15:21 ` [PATCH v3 17/17] Use scope_exit in regcache.c Pedro Alves
2019-01-23 15:21 ` [PATCH v3 08/17] Remove delete_just_stopped_threads_infrun_breakpoints_cleanup Pedro Alves
2019-01-23 15:21 ` [PATCH v3 01/17] Rename ESC -> ESC_PARENS Pedro Alves
2019-01-23 15:21 ` [PATCH v3 10/17] Remove cleanup_delete_std_terminate_breakpoint Pedro Alves
2019-01-23 15:21 ` [PATCH v3 15/17] Update an obsolete cleanup comment Pedro Alves
2019-01-23 15:21 ` [PATCH v3 12/17] Remove clear_symtab_users_cleanup Pedro Alves
2019-01-23 15:21 ` [PATCH v3 16/17] Update cleanup comment in ui-out.h Pedro Alves
2019-01-23 15:21 ` [PATCH v3 14/17] Remove remaining cleanup from fetch_inferior_event Pedro Alves
2019-01-23 15:21 ` [PATCH v3 06/17] Replace delete_longjmp_breakpoint_cleanup with a forward_scope_exit type Pedro Alves
2019-01-23 15:21 ` [PATCH v3 04/17] Use forward_scope_exit for scoped_finish_thread_state Pedro Alves
2019-01-23 17:10   ` Tom Tromey
2019-01-23 15:21 ` [PATCH v3 03/17] Introduce forward_scope_exit Pedro Alves
2019-01-23 15:21 ` [PATCH v3 13/17] Remove cleanup from stop_all_threads Pedro Alves
2019-01-23 15:28 ` [PATCH v3 09/17] Remove make_bpstat_clear_actions_cleanup Pedro Alves
2019-01-23 17:33 ` [PATCH v3 00/17] Remove some cleanups using scope_exit Tom Tromey
2019-01-23 19:18   ` Pedro Alves

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190123152131.29893-3-palves@redhat.com \
    --to=palves@redhat.com \
    --cc=andrew.burgess@embecosm.com \
    --cc=gdb-patches@sourceware.org \
    --cc=tom@tromey.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox