On Fri, Oct 13, 2017 at 2:40 AM, David Malcolm <dmalc...@redhat.com> wrote: > From: Trevor Saunders <tbsaunde+...@tbsaunde.org> > > I had a go at updating Trevor's unique_ptr patch from July > ( https://gcc.gnu.org/ml/gcc-patches/2017-07/msg02084.html ) > > One of the sticking points was what to call the namespace; there was > wariness about using "gtl" as the name. > > Richi proposed (https://gcc.gnu.org/ml/gcc-patches/2017-09/msg00155.html): >> If it should be short use g::. We can also use gnu:: I guess and I >> agree gnutools:: is a little long (similar to libiberty::). Maybe >> gt:: as a short-hand for gnutools. > > Pedro noted (https://gcc.gnu.org/ml/gcc-patches/2017-09/msg00157.html): >> Exactly 3 letters has the nice property of making s/gtl::foo/std::foo/ >> super trivial down the road; you don't have to care about reindenting >> stuff > > Hence this version of the patch uses "gnu::" - 3 letters, one of the > ones Richi proposed, and *not* a match for ".tl" (e.g. "gtl"); > (FWIW personally "gnu::" is my favorite, followed by "gcc::"). > > The include/unique-ptr.h in this patch is identical to that posted > by Trevor in July, with the following changes (by me): > - renaming of "gtl" to "gnu" > - renaming of DEFINE_GDB_UNIQUE_PTR to DEFINE_GNU_UNIQUE_PTR > - renaming of xfree_deleter to xmalloc_deleter, and making it > use "free" rather than "xfree" (which doesn't exist) > > I also went and added a gcc/unique-ptr-tests.cc file containing > selftests (my thinking here is that although std::unique_ptr ought > to already be well-tested, we need to ensure that the fallback > implementation is sane when building with C++ prior to C++11). > > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu, > using gcc 4.8 for the initial bootstrap (hence testing both gnu+03 > and then gnu++14 in the selftests, for stage 1 and stages 2 and 3 > respectively). > > I also manually tested selftests with both gcc 4.8 and trunk on > the same hardware (again, to exercise both the with/without C++11 > behavior). > > Tested with "make selftest-valgrind" (no new issues). > > OK for trunk?
Ok if the gdb folks approve (and will change to this version). Thanks, Richard. > Motivation/use-cases: > (a) I have an updated version of the > name_hint/deferred_diagnostic patch kit, which uses this (rather > than the refcounting used by > https://gcc.gnu.org/ml/gcc-patches/2017-05/msg00439.html ); and > (b) having this available ought to allow various other cleanups > e.g. the example ones identified by Trevor here: > https://gcc.gnu.org/ml/gcc-patches/2017-07/msg02085.html > > Thanks > Dave > > > Blurb from Trevor: > > For most of the history of this see > https://sourceware.org/ml/gdb-patches/2016-10/msg00223.html > The changes are mostly s/gdb/gtl/g > > include/ChangeLog: > > 2017-07-29 Trevor Saunders <tbsaunde+...@tbsaunde.org> > > * unique-ptr.h: New file. > > Combined ChangeLog follows: > > gcc/ChangeLog: > > David Malcolm <dmalc...@redhat.com> > > * Makefile.in (OBJS): Add unique-ptr-tests.o. > * selftest-run-tests.c (selftest::run_tests): Call > selftest::unique_ptr_tests_cc_tests. > * selftest.h (selftest::unique_ptr_tests_cc_tests): New decl. > * unique-ptr-tests.cc: New file. > > include/ChangeLog: > > Trevor Saunders <tbsaunde+...@tbsaunde.org> > David Malcolm <dmalc...@redhat.com> > > * unique-ptr.h: New file. > --- > gcc/Makefile.in | 1 + > gcc/selftest-run-tests.c | 1 + > gcc/selftest.h | 1 + > gcc/unique-ptr-tests.cc | 177 ++++++++++++++++++++++ > include/unique-ptr.h | 386 > +++++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 566 insertions(+) > create mode 100644 gcc/unique-ptr-tests.cc > create mode 100644 include/unique-ptr.h > > diff --git a/gcc/Makefile.in b/gcc/Makefile.in > index 878ce7b..2809619 100644 > --- a/gcc/Makefile.in > +++ b/gcc/Makefile.in > @@ -1568,6 +1568,7 @@ OBJS = \ > tree-vrp.o \ > tree.o \ > typed-splay-tree.o \ > + unique-ptr-tests.o \ > valtrack.o \ > value-prof.o \ > var-tracking.o \ > diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c > index 30e476d..b05e0fc 100644 > --- a/gcc/selftest-run-tests.c > +++ b/gcc/selftest-run-tests.c > @@ -66,6 +66,7 @@ selftest::run_tests () > sreal_c_tests (); > fibonacci_heap_c_tests (); > typed_splay_tree_c_tests (); > + unique_ptr_tests_cc_tests (); > > /* Mid-level data structures. */ > input_c_tests (); > diff --git a/gcc/selftest.h b/gcc/selftest.h > index 96eccac..adc0b68 100644 > --- a/gcc/selftest.h > +++ b/gcc/selftest.h > @@ -194,6 +194,7 @@ extern void store_merging_c_tests (); > extern void typed_splay_tree_c_tests (); > extern void tree_c_tests (); > extern void tree_cfg_c_tests (); > +extern void unique_ptr_tests_cc_tests (); > extern void vec_c_tests (); > extern void wide_int_cc_tests (); > extern void predict_c_tests (); > diff --git a/gcc/unique-ptr-tests.cc b/gcc/unique-ptr-tests.cc > new file mode 100644 > index 0000000..df18467 > --- /dev/null > +++ b/gcc/unique-ptr-tests.cc > @@ -0,0 +1,177 @@ > +/* Unit tests for unique-ptr.h. > + Copyright (C) 2017 Free Software Foundation, Inc. > + > +This file is part of GCC. > + > +GCC 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, or (at your option) any later > +version. > + > +GCC 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 GCC; see the file COPYING3. If not see > +<http://www.gnu.org/licenses/>. */ > + > +#include "config.h" > +#include "system.h" > +#include "coretypes.h" > +#include "unique-ptr.h" > +#include "selftest.h" > + > +#if CHECKING_P > + > +namespace selftest { > + > +namespace { > + > +/* A class for counting ctor and dtor invocations. */ > + > +struct stats > +{ > + stats () : ctor_count (0), dtor_count (0) {} > + > + int ctor_count; > + int dtor_count; > +}; > + > +/* A class that uses "stats" to track its ctor and dtor invocations. */ > + > +class foo > +{ > +public: > + foo (stats &s) : m_s (s) { ++m_s.ctor_count; } > + ~foo () { ++m_s.dtor_count; } > + > + int example_method () const { return 42; } > + > +private: > + foo (const foo&); > + foo & operator= (const foo &); > + > +private: > + stats &m_s; > +}; > + > +} // anonymous namespace > + > +/* Verify that the default ctor inits ptrs to NULL. */ > + > +static void > +test_null_ptr () > +{ > + gnu::unique_ptr<void *> p; > + ASSERT_EQ (NULL, p); > + > + gnu::unique_xmalloc_ptr<void *> q; > + ASSERT_EQ (NULL, q); > +} > + > +/* Verify that deletion happens when a unique_ptr goes out of scope. */ > + > +static void > +test_implicit_deletion () > +{ > + stats s; > + ASSERT_EQ (0, s.ctor_count); > + ASSERT_EQ (0, s.dtor_count); > + > + { > + gnu::unique_ptr<foo> f (new foo (s)); > + ASSERT_NE (NULL, f); > + ASSERT_EQ (1, s.ctor_count); > + ASSERT_EQ (0, s.dtor_count); > + } > + > + /* Verify that the foo was implicitly deleted. */ > + ASSERT_EQ (1, s.ctor_count); > + ASSERT_EQ (1, s.dtor_count); > +} > + > +/* Verify that we can assign to a NULL unique_ptr. */ > + > +static void > +test_overwrite_of_null () > +{ > + stats s; > + ASSERT_EQ (0, s.ctor_count); > + ASSERT_EQ (0, s.dtor_count); > + > + { > + gnu::unique_ptr<foo> f; > + ASSERT_EQ (NULL, f); > + ASSERT_EQ (0, s.ctor_count); > + ASSERT_EQ (0, s.dtor_count); > + > + /* Overwrite with a non-NULL value. */ > + f = gnu::unique_ptr<foo> (new foo (s)); > + ASSERT_EQ (1, s.ctor_count); > + ASSERT_EQ (0, s.dtor_count); > + } > + > + /* Verify that the foo is implicitly deleted. */ > + ASSERT_EQ (1, s.ctor_count); > + ASSERT_EQ (1, s.dtor_count); > +} > + > +/* Verify that we can assign to a non-NULL unique_ptr. */ > + > +static void > +test_overwrite_of_non_null () > +{ > + stats s; > + ASSERT_EQ (0, s.ctor_count); > + ASSERT_EQ (0, s.dtor_count); > + > + { > + gnu::unique_ptr<foo> f (new foo (s)); > + ASSERT_NE (NULL, f); > + ASSERT_EQ (1, s.ctor_count); > + ASSERT_EQ (0, s.dtor_count); > + > + /* Overwrite with a different value. */ > + f = gnu::unique_ptr<foo> (new foo (s)); > + ASSERT_EQ (2, s.ctor_count); > + ASSERT_EQ (1, s.dtor_count); > + } > + > + /* Verify that the 2nd foo was implicitly deleted. */ > + ASSERT_EQ (2, s.ctor_count); > + ASSERT_EQ (2, s.dtor_count); > +} > + > +/* Verify that unique_ptr's overloaded ops work. */ > + > +static void > +test_overloaded_ops () > +{ > + stats s; > + gnu::unique_ptr<foo> f (new foo (s)); > + ASSERT_EQ (42, f->example_method ()); > + ASSERT_EQ (42, (*f).example_method ()); > + ASSERT_EQ (f, f); > + ASSERT_NE (NULL, f.get ()); > + > + gnu::unique_ptr<foo> g (new foo (s)); > + ASSERT_NE (f, g); > +} > + > +/* Run all of the selftests within this file. */ > + > +void > +unique_ptr_tests_cc_tests () > +{ > + test_null_ptr (); > + test_implicit_deletion (); > + test_overwrite_of_null (); > + test_overwrite_of_non_null (); > + test_overloaded_ops (); > +} > + > +} // namespace selftest > + > +#endif /* #if CHECKING_P */ > diff --git a/include/unique-ptr.h b/include/unique-ptr.h > new file mode 100644 > index 0000000..eddb001 > --- /dev/null > +++ b/include/unique-ptr.h > @@ -0,0 +1,386 @@ > +/* gnu::unique_ptr, a simple std::unique_ptr replacement for C++03. > + > + Copyright (C) 2007-2016 Free Software Foundation, Inc. > + > + This file is part of GCC. > + > + 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/>. */ > + > +/* gnu::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, gnu::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 gnu::unique_ptr partial > + specialization that uses a custom (stateless) deleter: > + gnu::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 gnu::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, gnu::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 GNU_UNIQUE_PTR_H > +#define GNU_UNIQUE_PTR_H 1 > + > +#include <memory> > + > +namespace gnu > +{ > + > +#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 gnu::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: > + > + gnu::unique_ptr<foo> func (....) > + { > + return NULL; > + } > + > + and assignment from NULL: > + > + gnu::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_GNU_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 gnu::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_GNU_UNIQUE_PTR (unique_ptr) > + > +public: > + /* Dereferencing. */ > + T &operator* () const throw () { return *this->get (); } > + T *operator-> () const throw () { return this->get (); } > +}; > + > +/* Define gnu::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_GNU_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 gnu::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 gnu::unique_xmalloc_ptr, a gnu::unique_ptr that manages > + xmalloc'ed memory. */ > + > +/* The deleter for gnu::unique_xmalloc_ptr. Uses free. */ > +template <typename T> > +struct xmalloc_deleter > +{ > + void operator() (T *ptr) const { free (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, xmalloc_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, xmalloc_deleter<T> > > +{ > + typedef unique_ptr<T, xmalloc_deleter<T> > base_type; > + > + DEFINE_GNU_UNIQUE_PTR (unique_xmalloc_ptr) > +}; > + > +#endif /* C++11 */ > + > +} /* namespace gnu */ > + > +#endif /* GNU_UNIQUE_PTR_H */ > -- > 1.8.5.3 >