This is the mail archive of the gdb-cvs@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Introduce scope_exit


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=54b65c9b51ec80ef5812dde1a508ce171eeb8ce6

commit 54b65c9b51ec80ef5812dde1a508ce171eeb8ce6
Author: Pedro Alves <palves@redhat.com>
Date:   Wed Jan 23 18:58:29 2019 +0000

    Introduce scope_exit
    
    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:
    2019-01-23  Pedro Alves  <palves@redhat.com>
    	    Andrew Burgess  <andrew.burgess@embecosm.com>
    	    Tom Tromey  <tom@tromey.com>
    
    	* common/scope-exit.h: New file.

Diff:
---
 gdb/ChangeLog           |   6 ++
 gdb/common/scope-exit.h | 186 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 192 insertions(+)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 60c1923..996ca18 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,4 +1,10 @@
 2019-01-23  Pedro Alves  <palves@redhat.com>
+	    Andrew Burgess  <andrew.burgess@embecosm.com>
+	    Tom Tromey  <tom@tromey.com>
+
+	* common/scope-exit.h: New file.
+
+2019-01-23  Pedro Alves  <palves@redhat.com>
 
 	* common/preprocessor.h (ESC): Rename to ...
 	(ESC_PARENS): ... this.
diff --git a/gdb/common/scope-exit.h b/gdb/common/scope-exit.h
new file mode 100644
index 0000000..8cdbec3
--- /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 */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]