This is the mail archive of the gdb-patches@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]

[PATCH 1/3] Introduce gdb::unique_ptr


Many make_cleanup uses in the code base are best eliminated by using a
"owning" smart pointer to manage ownership of the resource
automatically.

The question is _which_ smart pointer.

We have std::auto_ptr in C++03, but, as is collective wisdom by now,
that's too easy to misuse, and has therefore been deprecated in C++11
and finally removed in C++17.

It'd be nice to be able to use std::unique_ptr instead, which is the
modern, safe std::auto_ptr replacement in C++11.

In addition to extra safety -- ownership transfer must be explicit --
std::unique_ptr has (among others) one nice feature that std::auto_ptr
doesn't --- ability to specify a custom deleter as template parameter.
In gdb's context, that allows easily creating a smart pointer for
memory allocated with xmalloc -- the smart pointer then knows to
release with xfree instead of delete.  This is particularly
interesting when managing objects allocated in C libraries, and also,
for C++-fying parts of GDB that interact with other parts that still
return object allocated with malloc.

Since std::unique_ptr is supposedly mostly a drop-in replacement for
std::auto_ptr -- basically "upgrading" is usually a matter of
find/replace and then fix the "bad" implicit ownership transfer cases,
I thought of actually taking advantage of std::unique_ptr when GDB is
compiled with a C++11 compiler.  And then since we don't require C++11
yet, I wrote a very lightweight std::unique_ptr "emulation" for C++03.
The emulation started out as a copy of GCC 7's std::auto_ptr, and then
heavilly customized to make it behave more like std::unique_ptr:

 - unique_ptr<T[]> specialization.  auto_ptr<T> does not know to use
   delete[].

 - custom deleters (though only stateless deleters --- support for
   stateful deleters could be added, but I saw no need for those at
   this point).

 - support for all of 'ptr != NULL', 'ptr == NULL' and 'if (ptr)'
   using the safe bool idiom.

 - initialization and assignment from NULL (std::unique_ptr allows
   "ptr = nullptr" instead, while std::auto_ptr allows neither.)

The "emulation" isn't perfect in the sense that just like
std::auto_ptr, it defines a copy constructor with greedy ownership
transfer.  However, since if compiling gdb with a C++11 (or newer)
compiler, we're actually using the real std::unique_ptr, as long as
GDB builds with GCC 6 or later, or any other compiler that defaults to
C++11 or later, then we know we're not misusing the C++03 version.

I thought of putting the "emulation" / shim in the "std" namespace, so
that when we start requiring C++11 at some point, no actual changes to
users of the smart pointer throughout would be necessary.  Putting
things in the std namespace is technically undefined, however in
practice it doesn't cause any issue with any compiler.  However,
thinking that people might be confused with seeing std::unique_ptr and
thinking that we're actually requiring C++11 already, I put the new
types in the "gdb" namespace instead.

For managing malloc pointers, this adds a gdb::unique_malloc_ptr<T>
"specialization" with a custom xfree deleter.

No actual use of any smart pointer is introduced in this patch.
That'll be done in following patches.

Tested (along with the rest of the series) on:

 - NetBSD 5.1 (gcc70 on the compile farm), w/ gcc 4.1.3
 - x86-64 Fedora 23, gcc 5.3.1 (gnu++03)
 - x86-64 Fedora 23, and gcc 7.0 (gnu++14)

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* common/common-defs.h: Include "gdb_unique_ptr.h".
	* common/gdb_unique_ptr.h: New.
	* common/safe-bool.h: New.
---
 gdb/common/common-defs.h    |   3 +
 gdb/common/gdb_unique_ptr.h | 363 ++++++++++++++++++++++++++++++++++++++++++++
 gdb/common/safe-bool.h      |  67 ++++++++
 3 files changed, 433 insertions(+)
 create mode 100644 gdb/common/gdb_unique_ptr.h
 create mode 100644 gdb/common/safe-bool.h

diff --git a/gdb/common/common-defs.h b/gdb/common/common-defs.h
index 0d8d100..52ecb08 100644
--- a/gdb/common/common-defs.h
+++ b/gdb/common/common-defs.h
@@ -79,4 +79,7 @@
 #define EXTERN_C_PUSH extern "C" {
 #define EXTERN_C_POP }
 
+/* Pull in gdb::unique_ptr and gdb::unique_malloc_ptr.  */
+#include "common/gdb_unique_ptr.h"
+
 #endif /* COMMON_DEFS_H */
diff --git a/gdb/common/gdb_unique_ptr.h b/gdb/common/gdb_unique_ptr.h
new file mode 100644
index 0000000..1cda298
--- /dev/null
+++ b/gdb/common/gdb_unique_ptr.h
@@ -0,0 +1,363 @@
+/* gdb::unique_ptr, a simple std::unique_ptr replacement for C++03.
+
+   Copyright (C) 2007-2016 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/>.  */
+
+/* gdb::unique_ptr maps to std::unique_ptr in C++11 mode, and to a
+   simplified emulation in C++03 mode.
+
+   The emulation code was originally based on GCC 7.0's std::auto_ptr
+   and then heavily customized to behave more like std::unique_ptr
+   (T[] specialization, assignment from NULL, explicit bool
+   conversion, etc.).
+
+   Our emulation actually lets you shoot yourself in the foot, just
+   like std::auto_ptr, since the copy ctor actually moves, but we know
+   that if gdb builds in C++11 mode, then we're not doing anything
+   unsafe.
+
+   Note, our emulation does NOT support "stateful" custom deleters.
+   You can't pass a deleter argument to the constructor.  Only the
+   managed pointer is stored.  Turns out that we don't really need
+   stateful custom deleters in practice.
+
+   At the end of the file you'll find a gdb::unique_ptr specialization
+   that uses a custom (stateless) deleter -- gdb::unique_malloc_ptr.
+*/
+
+#ifndef GDB_UNIQUE_PTR_H
+#define GDB_UNIQUE_PTR_H 1
+
+#include <memory>
+
+#include "safe-bool.h"
+
+namespace gdb
+{
+
+#if __cplusplus >= 201103
+
+/* In C++ mode, all we need is import the standard
+   std::unique_ptr.  */
+template<typename T> using unique_ptr
+  = std::unique_ptr<T>;
+
+/* Pull in move as well.  */
+using std::move;
+
+#else /* C++11 */
+
+/* Default destruction policy used by gdb::unique_ptr when no
+   deleter is specified.  Uses delete.  */
+
+template<typename T>
+struct default_delete
+{
+  void operator () (T *ptr) const { delete ptr; }
+};
+
+/* Specialization for arrays.  Uses delete[].  */
+
+template<typename T>
+struct default_delete<T[]>
+{
+  void operator () (T *ptr) const { delete [] ptr; }
+};
+
+/* Type used to support assignment from NULL:
+
+     gdb::unique_ptr<foo> ptr (....);
+     ...
+     ptr = NULL;
+*/
+struct unique_ptr_nullptr_t
+{
+private:
+  struct private_type;
+public:
+  /* Since null_type is private, the only way to construct this class
+     is by passing a NULL pointer.  See unique_ptr_base::operator=
+     further below.  */
+  unique_ptr_nullptr_t (private_type *) {}
+};
+
+/* Base class of our unique_ptr emulation.  Contains code common to
+   both the unique_ptr<T, D> and unique_ptr<T[], D>.  */
+
+template<typename T, typename D>
+class unique_ptr_base : public safe_bool <unique_ptr_base<T, D> >
+{
+public:
+  typedef T *pointer;
+  typedef T element_type;
+  typedef D deleter_type;
+
+  template <typename T1>
+  struct unique_ptr_base_ref
+  {
+    T1 *m_ptr;
+
+    explicit unique_ptr_base_ref (T1 *p): m_ptr (p) {}
+  };
+
+  typedef unique_ptr_base_ref<T> ref_type;
+
+  /* An unique_ptr is usually constructed from a raw pointer.  P - a
+     pointer (defaults to NULL).  This object now owns the object
+     pointed to by P.  */
+  explicit unique_ptr_base (element_type *p = NULL) throw() : m_ptr (p) {}
+
+  /* Even though std::unique_ptr is not copyable, our little simpler
+     emulation allows it, because RVO/NRVO requires an accessible copy
+     constructor, and also because our move emulation relies on this.
+
+     An unique_ptr_base can be constructed from another
+     unique_ptr_base.  A is another unique_ptr_base of the same type.
+
+     This object now owns the object previously owned by A, which has
+     given up ownership.  */
+  unique_ptr_base (unique_ptr_base& a) throw() : m_ptr (a.release ()) {}
+
+  /* Similarly, we implement this simply to allow std::swap work
+     without having to provide our own implementation.  We know that
+     if GDB compiles with real std::unique_ptr, this won't be called
+     "incorrectly".
+
+     Assignment operator.  A is another unique_ptr_base of the same
+     type.  This object now owns the object previously owned by A,
+     which has given up ownership.  The object that this one used to
+     own and track has been deleted.  */
+  unique_ptr_base&
+  operator= (unique_ptr_base &a) throw()
+  {
+    reset (a.release ());
+    return *this;
+  }
+
+  /* std::unique_ptr does not allow assignment, except from nullptr.
+     nullptr doesn't exist before C++11, so we allowing assignment
+     from NULL instead:
+       ptr = NULL;
+  */
+  unique_ptr_base &
+  operator= (const unique_ptr_nullptr_t &) throw()
+  {
+    reset ();
+    return *this;
+  }
+
+  /* When the unique_ptr_base goes out of scope, the object it owns is
+     deleted.  If it no longer owns anything (i.e., get() is NULL,
+     then this has no effect.  */
+  ~unique_ptr_base () { call_deleter (); }
+
+  /* "explicit operator bool" emulation using the safe bool idiom.  */
+  bool explicit_operator_bool () const
+  {
+    return m_ptr != NULL;
+  }
+
+  /* Bypassing the smart pointer.
+     Returns the raw pointer being managed.
+
+     You can get a copy of the pointer that this object owns, for
+     situations such as passing to a function which only accepts a raw
+     pointer.  Note, this smarts pointer still owns the memory.  */
+  element_type *get () const throw() { return m_ptr; }
+
+  /* Bypassing the smart pointer.  Returns the raw pointer being
+     managed.
+
+     You can get a copy of the pointer that this object owns, for
+     situations such as passing to a function which only accepts a raw
+     pointer.
+
+     Note, this smart pointer no longer owns the memory.  When this
+     object goes out of scope, nothing will happen.  */
+  element_type *release () throw()
+  {
+    pointer tmp = m_ptr;
+    m_ptr = NULL;
+    return tmp;
+  }
+
+  /* Forcibly delete the managed object.  P is a pointer (defaults to
+     NULL).  This object now owns the object pointed to by P.  The
+     previous object has been deleted.  */
+  void reset (element_type *p = NULL) throw()
+  {
+    if (p != m_ptr)
+      {
+	call_deleter ();
+	m_ptr = p;
+      }
+  }
+
+  /* Automatic conversions.
+
+     These operations convert an unique_ptr_base into and from an
+     unique_ptr_base_ref automatically as needed.  This allows
+     constructs such as:
+
+      unique_ptr<Derived>  func_returning_unique_ptr (.....);
+      ...
+      unique_ptr<Base> ptr = func_returning_unique_ptr (.....);
+   */
+  unique_ptr_base (unique_ptr_base_ref<element_type> ref) throw()
+    : m_ptr (ref.m_ptr) {}
+
+  unique_ptr_base &
+  operator= (unique_ptr_base_ref<element_type> ref) throw()
+  {
+    if (ref.m_ptr != this->get())
+      {
+	call_deleter ();
+	m_ptr = ref.m_ptr;
+      }
+    return *this;
+  }
+
+  template<typename T1>
+  operator unique_ptr_base_ref<T1> () throw()
+  { return unique_ptr_base_ref<T1> (this->release ()); }
+
+  template<typename T1, typename D1>
+  operator unique_ptr_base<T1, D1> () throw()
+  { return unique_ptr_base<T1, D1> (this->release ()); }
+
+private:
+
+  /* Call the deleter.  Note we assume the deleter is "stateless".  */
+  void call_deleter ()
+  {
+    D d;
+
+    d (m_ptr);
+  }
+
+  element_type *m_ptr;
+};
+
+/* Macro used to create a unique_ptr_base "specialization" -- a
+   subclass that uses a specific deleter.  Basically this re-defines
+   the necessary constructors.  This is necessary because we can't
+   inherit constructors with "using" without C++11.  While at it, we
+   inherit the assignment operator.  TYPE is the name of the type
+   being defined.  Assumes that 'base_type' is a typedef of the
+   baseclass UNIQUE_PTR is inheriting from.  */
+#define DEFINE_UNIQUE_PTR(TYPE)					\
+  public:								\
+  explicit TYPE (T *p = NULL) throw()					\
+    : base_type (p) {}							\
+  TYPE (typename base_type::ref_type ref) throw()			\
+    : base_type (ref.m_ptr) {}						\
+									\
+  using base_type::operator=;
+
+/* Finally, define gdb::unique_ptr.  */
+
+template <typename T, typename D = default_delete<T> >
+class unique_ptr : public unique_ptr_base<T, D>
+{
+  typedef unique_ptr_base<T, D> base_type;
+
+  DEFINE_UNIQUE_PTR (unique_ptr)
+
+  /* Dereferencing.  */
+  T &operator* () const throw() { return *this->get (); }
+  T *operator-> () const throw() { return this->get (); }
+};
+
+/* gdb::unique_ptr specialization for T[].  */
+
+template <typename T, typename D>
+class unique_ptr<T[], D> : public unique_ptr_base<T, D>
+{
+  typedef unique_ptr_base<T, D> base_type;
+
+  DEFINE_UNIQUE_PTR (unique_ptr)
+
+  /* Indexing operator.  */
+  T& operator[] (size_t i) const
+  { return this->get ()[i]; }
+};
+
+/* Comparison operators.  */
+
+template <typename T, typename D,
+	  typename U, typename E>
+inline bool
+operator== (const unique_ptr_base<T, D>& x,
+	    const unique_ptr_base<U, E>& y)
+{ return x.get() == y.get(); }
+
+template <typename T, typename D,
+	  typename U, typename E>
+inline bool
+operator!= (const unique_ptr_base<T, D>& x,
+	    const unique_ptr_base<U, E>& y)
+{ return x.get() != y.get(); }
+
+/* std::move "emulation".  This is as simple as it can be -- relies on
+   the fact that our std::unique_ptr emulation actually behaves like
+   std::auto_ptr -- copy/assignment actually moves.  */
+
+template<typename T, typename D>
+unique_ptr_base<T, D>
+move (unique_ptr_base<T, D> v)
+{
+  return v;
+}
+
+#endif /* C++11 */
+
+/* Define gdb::unique_malloc_ptr, a gdb::unique_ptr that manages
+   malloc'ed memory.  */
+
+/* The deleter for gdb::unique_malloc_ptr.  Uses xfree.  */
+template <typename T>
+struct xfree_deleter
+{
+  void operator() (T *ptr) const { xfree (ptr); }
+};
+
+#if __cplusplus >= 201103
+
+/* In C++11, we just import the standard unique_ptr to our
+   namespace with a custom deleter.  */
+
+template<typename T> using unique_malloc_ptr
+  = std::unique_ptr<T, xfree_deleter<T>>;
+
+#else /* C++11 */
+
+/* In C++03 mode, we need to define a subclass instead (and re-define
+   the constructors).  */
+
+template <typename T>
+class unique_malloc_ptr : public unique_ptr<T, xfree_deleter<T> >
+{
+  typedef unique_ptr<T, xfree_deleter<T> > base_type;
+
+  DEFINE_UNIQUE_PTR (unique_malloc_ptr)
+};
+
+#endif /* C++11 */
+
+} /* namespace gdb */
+
+#endif /* GDB_UNIQUE_PTR_H */
diff --git a/gdb/common/safe-bool.h b/gdb/common/safe-bool.h
new file mode 100644
index 0000000..b0075c3
--- /dev/null
+++ b/gdb/common/safe-bool.h
@@ -0,0 +1,67 @@
+/* Safe bool idiom implementation.
+
+   Copyright (C) 2016 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 SAFE_BOOL_H
+#define SAFE_BOOL_H 1
+
+/* Helper classes used to implement the safe bool idiom, for compilers
+   that don't support "explicit operator bool()" (C++11).  Classes
+   that want to support explicit boolean conversion inherit from
+   safe_bool (using CRTP) and implement the explicit_operator_bool
+   method.  */
+
+class safe_bool_base
+{
+protected:
+  typedef void (safe_bool_base::*bool_type) () const;
+  void this_type_does_not_support_comparisons () const {}
+};
+
+/* Baseclass, using CRTP.  */
+
+template <typename T = void>
+class safe_bool : public safe_bool_base {
+public:
+  operator bool_type () const
+  {
+    return (static_cast<const T *>(this))->explicit_operator_bool ()
+      ? &safe_bool_base::this_type_does_not_support_comparisons : 0;
+  }
+
+protected:
+  ~safe_bool () {}
+};
+
+/* Comparison operators.  */
+
+template <typename T, typename U>
+bool operator== (const safe_bool<T> &lhs, const safe_bool<U> &rhs)
+{
+  lhs.this_type_does_not_support_comparisons ();
+  return false;
+}
+
+template <typename T,typename U>
+bool operator!= (const safe_bool<T> &lhs, const safe_bool<U> &rhs)
+{
+  lhs.this_type_does_not_support_comparisons ();
+  return false;
+}
+
+#endif /* SAFE_BOOL_H */
-- 
2.5.5


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