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 v3] Introduce gdb::unique_ptr


A bunch of comment typos jumped out at me when I read this in
the mail client.  Looks like my brain is conditioned to only
be able to review properly in the mail client by now...

Here's the same, but with those fixed.  Hopefully the makes
the intro text a bit clearer.

New in v3:

  - Comment typos fixed.

New in v2:

  - considering a move to C++11, it no longer makes much sense to add
    a base class just for the one instance of the safe bool idiom.
    That's now inlined into unique_ptr directly.

  - added missing </<=/>/>= comparison operators.

  - simplified the implementation even further, by removing the "ref"
    class, and adding converting constructors instead.  This actually
    fixes this use case:

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

    Looks like GCC's std::auto_ptr is buggy in that respect, because
    that doesn't actually work with std::auto_ptr, despite the
    comments indicating otherwise, and I was copying the comment/bug.

  - comments updated/rewritten throughout, including the git commit
    log, to hopefully clarify things.

  I think that the code looks clearer and less "magic" now.

I'd like to get this in soon to unblock Tromey's C++ series, and more.
We can move along with the C++11 discussion / plan in parallel.

Any objections?

I'll be more than happy to remove this when we finally make C++11 a
requirement.  But I don't want to rush that.  We should make sure that
the buildbot's buildslaves are ready, for example.

~~~

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 malloc.

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.

   - Assignment from NULL.

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

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

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

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

   - 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 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.
---
 gdb/common/common-defs.h    |   3 +
 gdb/common/gdb_unique_ptr.h | 357 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 360 insertions(+)
 create mode 100644 gdb/common/gdb_unique_ptr.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..7d455f5
--- /dev/null
+++ b/gdb/common/gdb_unique_ptr.h
@@ -0,0 +1,357 @@
+/* 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_malloc_ptr.  That is used to manage pointers to objects
+   allocated with malloc.
+
+   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, no attempt is used to use SFINAE to
+   limit 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 moving by mistake.
+
+   Putting gdb::unique_ptr in standard containers is not supported.
+*/
+
+#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 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 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.  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 &a) throw()
+    : m_ptr (const_cast <unique_ptr_base&> (a).release ()) {}
+
+  /* Converting "move" constructor.  Really an lvalue ref converting
+     constructor.  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> &a) throw()
+    : m_ptr (const_cast <unique_ptr_base<T1, D1>&> (a).release ()) {}
+
+  /* The "move" assignment operator.  Really an lvalue ref assignment
+     operator that actually moves.  See comments above.  */
+  unique_ptr_base &operator= (const unique_ptr_base &a) throw()
+  {
+    reset (const_cast <unique_ptr_base&> (a).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= (const unique_ptr_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::*bool_type) () const;
+  void this_type_does_not_support_comparisons () const {}
+
+public:
+  operator bool_type () 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_UNIQUE_PTR(TYPE)						\
+public:									\
+  explicit TYPE (T *p = NULL) throw()					\
+    : base_type (p) {}							\
+									\
+  TYPE (const TYPE &a) throw() : base_type (a) {}			\
+									\
+  template<typename T1, typename D1>					\
+  TYPE (const detail::unique_ptr_base<T1, D1> &a) throw()		\
+    : base_type (a) {}							\
+									\
+  using base_type::operator=;
+
+/* Define non-array 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_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 detail::unique_ptr_base<T, D>
+{
+  typedef detail::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 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_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, 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_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 */
-- 
2.5.5



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