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
This commit is contained in:
David Malcolm 2017-10-16 20:50:40 +00:00
parent 2de3d3c6a6
commit 46d2b77d71
7 changed files with 653 additions and 0 deletions

View File

@ -1,3 +1,11 @@
2017-10-16 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.
2017-10-16 Vladimir Makarov <vmakarov@redhat.com>
PR sanitizer/82353

View File

@ -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 \

View File

@ -67,6 +67,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 ();

View File

@ -195,6 +195,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 ();

234
gcc/unique-ptr-tests.cc Normal file
View File

@ -0,0 +1,234 @@
/* 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;
};
/* A struct for testing unique_ptr<T[]>. */
struct has_default_ctor
{
has_default_ctor () : m_field (42) {}
int m_field;
};
/* A dummy struct for testing unique_xmalloc_ptr. */
struct dummy
{
int field;
};
} // 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);
}
/* Verify that the gnu::unique_ptr specialization for T[] works. */
static void
test_array_new ()
{
const int num = 10;
gnu::unique_ptr<has_default_ctor[]> p (new has_default_ctor[num]);
ASSERT_NE (NULL, p.get ());
/* Verify that operator[] works, and that the default ctor was called
on each element. */
for (int i = 0; i < num; i++)
ASSERT_EQ (42, p[i].m_field);
}
/* Verify that gnu::unique_xmalloc_ptr works. */
static void
test_xmalloc ()
{
gnu::unique_xmalloc_ptr<dummy> p (XNEW (dummy));
ASSERT_NE (NULL, p.get ());
}
/* Verify the gnu::unique_xmalloc_ptr specialization for T[]. */
static void
test_xmalloc_array ()
{
const int num = 10;
gnu::unique_xmalloc_ptr<dummy[]> p (XNEWVEC (dummy, num));
ASSERT_NE (NULL, p.get ());
/* Verify that operator[] works. */
for (int i = 0; i < num; i++)
p[i].field = 42;
for (int i = 0; i < num; i++)
ASSERT_EQ (42, p[i].field);
}
/* 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 ();
test_array_new ();
test_xmalloc ();
test_xmalloc_array ();
}
} // namespace selftest
#endif /* #if CHECKING_P */

View File

@ -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>

403
include/unique-ptr.h Normal file
View File

@ -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 */