diff options
author | David Malcolm <dmalcolm@gcc.gnu.org> | 2017-10-16 20:50:40 +0000 |
---|---|---|
committer | David Malcolm <dmalcolm@gcc.gnu.org> | 2017-10-16 20:50:40 +0000 |
commit | 46d2b77d71c3c9a2ea326ae9c3a89a19de3254ca (patch) | |
tree | e11b3eda93410442b9c1c59e85c5147392d1728b /include | |
parent | 2de3d3c6a62ac4299e9f5a310e4bf8ff28dbd47f (diff) |
Add gnu::unique_ptr
This is a version of the patch posted by Trevor Saunders on 2017-07-31,
for which he wrote:
> 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
This version was updated by me (dmalcolm) adding these changes:
- renaming of "gtl" to "gnu" (3 letters, and one of the ones Richi
proposed, and not a match for "*tl")
- 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)
- added a gcc/unique-ptr-tests.cc
- implement unique_xmalloc_ptr<T[]> (taken from gdb, but changing
"xfree" to "free", and adding support for pre-C++-11)
gcc/ChangeLog:
David Malcolm <dmalcolm@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+gcc@tbsaunde.org>
David Malcolm <dmalcolm@redhat.com>
* unique-ptr.h: New file.
From-SVN: r253797
Diffstat (limited to 'include')
-rw-r--r-- | include/ChangeLog | 5 | ||||
-rw-r--r-- | include/unique-ptr.h | 403 |
2 files changed, 408 insertions, 0 deletions
diff --git a/include/ChangeLog b/include/ChangeLog index 0221586d778..805e81c0227 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,8 @@ +2017-10-16 Trevor Saunders <tbsaunde+gcc@tbsaunde.org> + David Malcolm <dmalcolm@redhat.com> + + * unique-ptr.h: New file. + 2017-09-15 Yao Qi <yao.qi@linaro.org> Pedro Alves <palves@redhat.com> diff --git a/include/unique-ptr.h b/include/unique-ptr.h new file mode 100644 index 00000000000..c5ca031c284 --- /dev/null +++ b/include/unique-ptr.h @@ -0,0 +1,403 @@ +/* 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); } +}; + +/* Same, for arrays. */ +template <typename T> +struct xmalloc_deleter<T[]> +{ + 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) +}; + +/* Define gnu::unique_xmalloc_ptr specialization for T[]. */ + +template <typename T> +class unique_xmalloc_ptr<T[]> : 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 */ |