Introduce gdb::unique_ptr
authorPedro Alves <palves@redhat.com>
Tue, 18 Oct 2016 10:42:35 +0000 (11:42 +0100)
committerPedro Alves <palves@redhat.com>
Tue, 18 Oct 2016 10:42:35 +0000 (11:42 +0100)
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.

GDB currently supports building with a C++03 compiler.  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 -- moving (i.e., transfer of ownership of
the managed pointer between smart pointers) 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 objects allocated with xmalloc.

Since std::unique_ptr's API is quite nice, and eventually we'd like to
move to C++11, this patch adds a C++03-compatible smart pointer that
exposes the subset of the std::unique_ptr API that we're interested
in.  An advantage is that whenever we start requiring C++11, we won't
have to learn a new API.  Meanwhile, this allows continuing to support
building with a C++03 compiler.

Since C++03 doesn't support rvalue references (boost gets close to
emulating them, but it's not fully transparent to user code), the
C++03 std::unique_ptr emulation here doesn't try hard to prevent
accidentally moving, which is where most of complication of a more
thorough emulation would be.  Instead, we rely on the fact that GDB
will be usually compiled with a C++11 compiler, and use the real
std::unique_ptr in that case to catch such accidental moves.  IOW, the
goal here is to allow code that would be correct using std::unique_ptr
to be equally correct in C++03 mode, and, just as efficient.

The C++03 version was originally based on GCC 7.0's std::auto_ptr and
then heavily customized to behave more like C++11's std::unique_ptr:

   - Support for custom (stateless) deleters.  (Support for stateful
     deleters could be added, if necessary.)

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

   - Support for all of 'ptr != NULL', 'ptr == NULL' and 'if (ptr)'
     using the safe bool idiom to emulate C++11's explicit bool
     operator.

   - There's no nullptr in C++03, so this allows initialization and
     assignment from NULL instead (std::auto_ptr allows neither).

   - Variable names un-uglified (ie., no leading __ prefix everywhere).

   - Formatting made to follow GDB's coding conventions, including
     comment style.

   - Converting "move" constructors done differently in order to truly
     support:

      unique_ptr<Derived> func_returning_unique_ptr (.....);
      ...
      unique_ptr<Base> ptr = func_returning_unique_ptr (.....);

At this point, it no longer shares much at all with the original file,
but, that's the history.

See comments in the code to find out more.

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 xmalloc pointers, this adds a gdb::unique_xmalloc_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:
2016-10-18  Pedro Alves  <palves@redhat.com>

* common/common-defs.h: Include "gdb_unique_ptr.h".
* common/gdb_unique_ptr.h: New.

gdb/ChangeLog
gdb/common/common-defs.h
gdb/common/gdb_unique_ptr.h [new file with mode: 0644]

index 4b642b8ae9eaa7bf84cd5a71651fd222781f0d0c..2ebad5921492884e897f9c76d609f66f9fa60b20 100644 (file)
@@ -1,3 +1,8 @@
+2016-10-18  Pedro Alves  <palves@redhat.com>
+
+       * common/common-defs.h: Include "gdb_unique_ptr.h".
+       * common/gdb_unique_ptr.h: New.
+
 2016-10-18  Maciej W. Rozycki  <macro@imgtec.com>
 
        * i386-tdep.c (i386_mpx_info_bounds): Make sure the architecture
index b82906f1ebc6fed652cd7145eb5e3d7d5c3cb098..5426dd5d98a0f324421b0e67bbe089790b0d42b1 100644 (file)
@@ -82,4 +82,7 @@
 #define EXTERN_C_PUSH extern "C" {
 #define EXTERN_C_POP }
 
+/* Pull in gdb::unique_ptr and gdb::unique_xmalloc_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 (file)
index 0000000..e881e38
--- /dev/null
@@ -0,0 +1,386 @@
+/* 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 defines a C++ owning smart pointer that exposes a
+   subset of the std::unique_ptr API.
+
+   In fact, when compiled with a C++11 compiler, gdb::unique_ptr
+   actually _is_ std::unique_ptr.  When compiled with a C++03 compiler
+   OTOH, it's an hand coded std::unique_ptr emulation that assumes
+   code is correct and doesn't try to be too smart.
+
+   This supports custom deleters, but not _stateful_ deleters, so you
+   can't use those in C++11 mode either.  Only the managed pointer is
+   stored in the smart pointer.  That could be changed; it simply
+   wasn't found necessary.
+
+   At the end of the file you'll find a gdb::unique_ptr partial
+   specialization that uses a custom (stateless) deleter:
+   gdb::unique_xmalloc_ptr.  That is used to manage pointers to
+   objects allocated with xmalloc.
+
+   The C++03 version was originally based on GCC 7.0's std::auto_ptr
+   and then heavily customized to behave more like C++11's
+   std::unique_ptr, but at this point, it no longer shares much at all
+   with the original file.  But, that's the history and the reason for
+   the copyright's starting year.
+
+   The C++03 version lets you shoot yourself in the foot, since
+   similarly to std::auto_ptr, the copy constructor and assignment
+   operators actually move.  Also, in the name of simplicity, no
+   effort is spent on using SFINAE to prevent invalid conversions,
+   etc.  This is not really a problem, because the goal here is to
+   allow code that would be correct using std::unique_ptr to be
+   equally correct in C++03 mode, and, just as efficient.  If client
+   code compiles correctly with a C++11 (or newer) compiler, we know
+   we're not doing anything invalid by mistake.
+
+   Usage notes:
+
+   - Putting gdb::unique_ptr in standard containers is not supported,
+     since C++03 containers are not move-aware (and our emulation
+     relies on copy actually moving).
+
+   - Since there's no nullptr in C++03, gdb::unique_ptr allows
+     implicit initialization and assignment from NULL instead.
+
+   - To check whether there's an associated managed object, all these
+     work as expected:
+
+      if (ptr)
+      if (!ptr)
+      if (ptr != NULL)
+      if (ptr == NULL)
+      if (NULL != ptr)
+      if (NULL == ptr)
+*/
+
+#ifndef GDB_UNIQUE_PTR_H
+#define GDB_UNIQUE_PTR_H 1
+
+#include <memory>
+
+namespace gdb
+{
+
+#if __cplusplus >= 201103
+
+/* In C++11 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; }
+};
+
+namespace detail
+{
+/* Type used to support implicit construction from NULL:
+
+     gdb::unique_ptr<foo> func (....)
+     {
+        return NULL;
+     }
+
+   and assignment from NULL:
+
+     gdb::unique_ptr<foo> ptr (....);
+     ...
+     ptr = NULL;
+
+  It is intentionally not defined anywhere.  */
+struct nullptr_t;
+
+/* Base class of our unique_ptr emulation.  Contains code common to
+   both unique_ptr<T, D> and unique_ptr<T[], D>.  */
+
+template<typename T, typename D>
+class unique_ptr_base
+{
+public:
+  typedef T *pointer;
+  typedef T element_type;
+  typedef D deleter_type;
+
+  /* Takes ownership of a pointer.  P is a pointer to an object of
+     element_type type.  Defaults to NULL.  */
+  explicit unique_ptr_base (element_type *p = NULL) throw () : m_ptr (p) {}
+
+  /* The "move" constructor.  Really a copy constructor that actually
+     moves.  Even though std::unique_ptr is not copyable, our little
+     simpler emulation allows it, because:
+
+       - There are no rvalue references in C++03.  Our move emulation
+         instead relies on copy/assignment moving, like std::auto_ptr.
+       - RVO/NRVO requires an accessible copy constructor
+  */
+  unique_ptr_base (const unique_ptr_base &other) throw ()
+    : m_ptr (const_cast<unique_ptr_base &> (other).release ()) {}
+
+  /* Converting "move" constructor.  Really an lvalue ref converting
+     constructor that actually moves.  This allows constructs such as:
+
+      unique_ptr<Derived> func_returning_unique_ptr (.....);
+      ...
+      unique_ptr<Base> ptr = func_returning_unique_ptr (.....);
+  */
+  template<typename T1, typename D1>
+  unique_ptr_base (const unique_ptr_base<T1, D1> &other) throw ()
+    : m_ptr (const_cast<unique_ptr_base<T1, D1> &> (other).release ()) {}
+
+  /* The "move" assignment operator.  Really an lvalue ref copy
+     assignment operator that actually moves.  See comments above.  */
+  unique_ptr_base &operator= (const unique_ptr_base &other) throw ()
+  {
+    reset (const_cast<unique_ptr_base &> (other).release ());
+    return *this;
+  }
+
+  /* Converting "move" assignment.  Really an lvalue ref converting
+     copy assignment operator that moves.  See comments above.  */
+  template<typename T1, typename D1>
+  unique_ptr_base &operator= (const unique_ptr_base<T1, D1> &other) throw ()
+  {
+    reset (const_cast<unique_ptr_base<T1, D1> &> (other).release ());
+    return *this;
+  }
+
+  /* std::unique_ptr does not allow assignment, except from nullptr.
+     nullptr doesn't exist in C++03, so we allow assignment from NULL
+     instead [ptr = NULL;].
+  */
+  unique_ptr_base &operator= (detail::nullptr_t *) throw ()
+  {
+    reset ();
+    return *this;
+  }
+
+  ~unique_ptr_base () { call_deleter (); }
+
+  /* "explicit operator bool ()" emulation using the safe bool
+     idiom.  */
+private:
+  typedef void (unique_ptr_base::*explicit_operator_bool) () const;
+  void this_type_does_not_support_comparisons () const {}
+
+public:
+  operator explicit_operator_bool () const
+  {
+    return (m_ptr != NULL
+           ? &unique_ptr_base::this_type_does_not_support_comparisons
+           : 0);
+  }
+
+  element_type *get () const throw () { return m_ptr; }
+
+  element_type *release () throw ()
+  {
+    pointer tmp = m_ptr;
+    m_ptr = NULL;
+    return tmp;
+  }
+
+  void reset (element_type *p = NULL) throw ()
+  {
+    if (p != m_ptr)
+      {
+       call_deleter ();
+       m_ptr = p;
+      }
+  }
+
+private:
+
+  /* Call the deleter.  Note we assume the deleter is "stateless".  */
+  void call_deleter ()
+  {
+    D d;
+
+    d (m_ptr);
+  }
+
+  element_type *m_ptr;
+};
+
+} /* namespace detail */
+
+/* Macro used to create a unique_ptr_base "partial specialization" --
+   a subclass that uses a specific deleter.  Basically this re-defines
+   the necessary constructors.  This is necessary because C++03
+   doesn't support inheriting constructors with "using".  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 TYPE is inheriting from.  */
+#define DEFINE_GDB_UNIQUE_PTR(TYPE)                                            \
+public:                                                                        \
+  explicit TYPE (T *p = NULL) throw ()                                 \
+    : base_type (p) {}                                                 \
+                                                                       \
+  TYPE (const TYPE &other) throw () : base_type (other) {}             \
+                                                                       \
+  TYPE (detail::nullptr_t *) throw () : base_type (NULL) {}            \
+                                                                       \
+  template<typename T1, typename D1>                                   \
+  TYPE (const detail::unique_ptr_base<T1, D1> &other) throw ()         \
+    : base_type (other) {}                                             \
+                                                                       \
+  using base_type::operator=;
+
+/* Define single-object gdb::unique_ptr.  */
+
+template <typename T, typename D = default_delete<T> >
+class unique_ptr : public detail::unique_ptr_base<T, D>
+{
+  typedef detail::unique_ptr_base<T, D> base_type;
+
+  DEFINE_GDB_UNIQUE_PTR (unique_ptr)
+
+public:
+  /* Dereferencing.  */
+  T &operator* () const throw () { return *this->get (); }
+  T *operator-> () const throw () { return this->get (); }
+};
+
+/* Define gdb::unique_ptr specialization for T[].  */
+
+template <typename T, typename D>
+class unique_ptr<T[], D> : public detail::unique_ptr_base<T, D>
+{
+  typedef detail::unique_ptr_base<T, D> base_type;
+
+  DEFINE_GDB_UNIQUE_PTR (unique_ptr)
+
+public:
+  /* 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 detail::unique_ptr_base<T, D> &x,
+           const detail::unique_ptr_base<U, E> &y)
+{ return x.get() == y.get(); }
+
+template <typename T, typename D,
+         typename U, typename E>
+inline bool
+operator!= (const detail::unique_ptr_base<T, D> &x,
+           const detail::unique_ptr_base<U, E> &y)
+{ return x.get() != y.get(); }
+
+template<typename T, typename D,
+        typename U, typename E>
+inline bool
+operator< (const detail::unique_ptr_base<T, D> &x,
+          const detail::unique_ptr_base<U, E> &y)
+{ return x.get() < y.get (); }
+
+template<typename T, typename D,
+        typename U, typename E>
+inline bool
+operator<= (const detail::unique_ptr_base<T, D> &x,
+           const detail::unique_ptr_base<U, E> &y)
+{ return !(y < x); }
+
+template<typename T, typename D,
+        typename U, typename E>
+inline bool
+operator> (const detail::unique_ptr_base<T, D> &x,
+          const detail::unique_ptr_base<U, E> &y)
+{ return y < x; }
+
+template<typename T, typename D,
+        typename U, typename E>
+inline bool
+operator>= (const detail::unique_ptr_base<T, D> &x,
+           const detail::unique_ptr_base<U, E> &y)
+{ return !(x < y); }
+
+/* std::move "emulation".  This is as simple as it can be -- no
+   attempt is made to emulate rvalue references.  Instead relies on
+   the fact that gdb::unique_ptr has move semantics like
+   std::auto_ptr.  I.e., copy/assignment actually moves.  */
+
+template<typename T, typename D>
+unique_ptr<T, D>
+move (unique_ptr<T, D> v)
+{
+  return v;
+}
+
+#endif /* C++11 */
+
+/* Define gdb::unique_xmalloc_ptr, a gdb::unique_ptr that manages
+   xmalloc'ed memory.  */
+
+/* The deleter for gdb::unique_xmalloc_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_xmalloc_ptr
+  = std::unique_ptr<T, xfree_deleter<T>>;
+
+#else /* C++11 */
+
+/* In C++03, we don't have template aliases, so we need to define a
+   subclass instead, and re-define the constructors, because C++03
+   doesn't support inheriting constructors either.  */
+
+template <typename T>
+class unique_xmalloc_ptr : public unique_ptr<T, xfree_deleter<T> >
+{
+  typedef unique_ptr<T, xfree_deleter<T> > base_type;
+
+  DEFINE_GDB_UNIQUE_PTR (unique_xmalloc_ptr)
+};
+
+#endif /* C++11 */
+
+} /* namespace gdb */
+
+#endif /* GDB_UNIQUE_PTR_H */