Patchwork [v3] (almost) finish <scoped_allocator>

login
register
mail settings
Submitter Jonathan Wakely
Date Oct. 19, 2012, 4:16 p.m.
Message ID <CAH6eHdSAC1oZxitijjmBKLQC8DeV8UeYL2q7TpQ+zCKY8-_x5w@mail.gmail.com>
Download mbox | patch
Permalink /patch/192768/
State New
Headers show

Comments

Jonathan Wakely - Oct. 19, 2012, 4:16 p.m.
This adds support for piecewise construction of std::pair by
scoped_allocator_adaptor.  The only thing missing from
scoped_allocator_adaptor now is that my definition of OUTERMOST isn't
recursive so doesn't work for nested scoped_allocator_adaptors.
That's a suitably obscure use case that I'm not going to rush to fix
it today.

        * include/std/scoped_allocator (__outermost_alloc_traits): Define.
        (scoped_allocator_adaptor::destroy): Use it.
        (scoped_allocator_adaptor::construct): Likewise. Overload for
        piecewise construction of std::pair objects.
        * testsuite/20_util/scoped_allocator/2.cc: New.

Tested x86_64-linux, committed to trunk.
commit 2a969ae20431833dbb4a2dfdf0be03b5d4b716c9
Author: Jonathan Wakely <jwakely.gcc@gmail.com>
Date:   Fri Oct 19 16:58:53 2012 +0100

    	* include/std/scoped_allocator (__outermost_alloc_traits): Define.
    	(scoped_allocator_adaptor::destroy): Use it.
    	(scoped_allocator_adaptor::construct): Likewise. Overload for
    	piecewise construction of std::pair objects.
    	* testsuite/20_util/scoped_allocator/2.cc: New.
    	* doc/xml/manual/status_cxx2011.xml: Update.
Jonathan Wakely - Oct. 20, 2012, 1:35 a.m.
On 19 October 2012 21:28, Hans-Peter Nilsson wrote:
> Looks like _U is one of those identifiers that should be
> avoided:  Grep yields:
> src/newlib/libc/include/ctype.h:#define _U      01

Argh! my bad, sorry - fix on the way ...

Patch

diff --git a/libstdc++-v3/doc/xml/manual/status_cxx2011.xml b/libstdc++-v3/doc/xml/manual/status_cxx2011.xml
index ba37e0e..226eef9 100644
--- a/libstdc++-v3/doc/xml/manual/status_cxx2011.xml
+++ b/libstdc++-v3/doc/xml/manual/status_cxx2011.xml
@@ -1037,7 +1037,7 @@  particular release.
       <entry>20.12.4</entry>
       <entry>Scoped allocator adaptor members</entry>
       <entry>Partial</entry>
-      <entry>Missing std::pair piecewise construction.</entry>
+      <entry>OUTERMOST is not recursive.</entry>
     </row>
     <row>
       <entry>20.12.5</entry>
diff --git a/libstdc++-v3/include/std/scoped_allocator b/libstdc++-v3/include/std/scoped_allocator
index fc2db7c..81365b6 100644
--- a/libstdc++-v3/include/std/scoped_allocator
+++ b/libstdc++-v3/include/std/scoped_allocator
@@ -74,7 +74,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : allocator_traits<_Alloc>::propagate_on_container_swap
     { };
 
-  
+
   template<typename _Alloc>
     inline auto
     __do_outermost(_Alloc& __a, _Alloc*) -> decltype(__a.outer_allocator())
@@ -85,6 +85,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     __do_outermost(_Alloc& __a, ...)
     { return __a; }
 
+  // TODO: make recursive (see note in 20.12.4/1)
   template<typename _Alloc>
     inline auto
     __outermost(_Alloc& __a) -> decltype(__do_outermost(__a, &__a))
@@ -190,15 +191,21 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_tie() const noexcept
       { return std::tuple_cat(std::tie(outer_allocator()), _M_inner._M_tie()); }
 
+      template<typename _Alloc>
+	using __outermost_type = typename
+	  std::decay<decltype(__outermost(std::declval<_Alloc&>()))>::type;
+
+      template<typename _Alloc>
+	using __outermost_alloc_traits
+	  = allocator_traits<__outermost_type<_Alloc>>;
       
       template<typename _Tp, typename... _Args>
         void 
         _M_construct(__uses_alloc0, _Tp* __p, _Args&&... __args)
         {
-          auto& __outer = __outermost(*this);
-	  typedef typename std::decay<decltype(__outer)>::type __outer_type;
-          typedef allocator_traits<__outer_type> __o_traits;
-          __o_traits::construct(__outer, __p, std::forward<_Args>(__args)...);
+	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
+	  _O_traits::construct(__outermost(*this), __p,
+			       std::forward<_Args>(__args)...);
         }
 
       typedef __uses_alloc1<typename __inner_type::__type> __uses_alloc1_;
@@ -208,22 +215,20 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
         void 
         _M_construct(__uses_alloc1_, _Tp* __p, _Args&&... __args)
         {
-          auto& __outer = __outermost(*this);
-	  typedef typename std::decay<decltype(__outer)>::type __outer_type;
-          typedef allocator_traits<__outer_type> __o_traits;
-          __o_traits::construct(__outer, __p, allocator_arg, inner_allocator(),
-                                std::forward<_Args>(__args)...);
+	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
+	  _O_traits::construct(__outermost(*this), __p,
+			       allocator_arg, inner_allocator(),
+			       std::forward<_Args>(__args)...);
         }
 
       template<typename _Tp, typename... _Args>
         void 
         _M_construct(__uses_alloc2_, _Tp* __p, _Args&&... __args)
         {
-	  auto& __outer = __outermost(*this);
-	  typedef typename std::decay<decltype(__outer)>::type __outer_type;
-          typedef allocator_traits<__outer_type> __o_traits;
-          __o_traits::construct(__outer, __p, std::forward<_Args>(__args)...,
-                                inner_allocator());
+	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
+	  _O_traits::construct(__outermost(*this), __p,
+			       std::forward<_Args>(__args)...,
+			       inner_allocator());
         }
 
       template<typename _Alloc>
@@ -338,15 +343,61 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
           _M_construct(__use_tag, __p, std::forward<_Args>(__args)...);
         }
 
-      // TODO: construct pairs
+      template<typename _T1, typename _T2, typename... _Args1,
+	       typename... _Args2>
+	void
+	construct(pair<_T1, _T2>* __p, piecewise_construct_t,
+		  tuple<_Args1...> __x, tuple<_Args2...> __y)
+	{
+	  auto& __inner = inner_allocator();
+	  auto __x_use_tag
+	    = __use_alloc<_T1, inner_allocator_type, _Args1...>(__inner);
+	  auto __y_use_tag
+	    = __use_alloc<_T2, inner_allocator_type, _Args2...>(__inner);
+	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
+	  _O_traits::construct(__outermost(*this), __p, piecewise_construct,
+			       _M_construct_p(__x_use_tag, __x),
+			       _M_construct_p(__y_use_tag, __y));
+	}
+
+      template<typename _T1, typename _T2>
+	void
+	construct(pair<_T1, _T2>* __p)
+	{ construct(__p, piecewise_construct, tuple<>(), tuple<>()); }
+
+      template<typename _T1, typename _T2, typename _U, typename _V>
+	void
+	construct(pair<_T1, _T2>* __p, _U&& __u, _V&& __v)
+	{
+	  construct(__p, piecewise_construct,
+		    std::forward_as_tuple(std::forward<_U>(__u)),
+		    std::forward_as_tuple(std::forward<_V>(__v)));
+	}
+
+      template<typename _T1, typename _T2, typename _U, typename _V>
+	void
+	construct(pair<_T1, _T2>* __p, const pair<_U, _V>& __x)
+	{
+	  construct(__p, piecewise_construct,
+		    std::forward_as_tuple(__x.first),
+		    std::forward_as_tuple(__x.second));
+	}
+
+      template<typename _T1, typename _T2, typename _U, typename _V>
+	void
+	construct(pair<_T1, _T2>* __p, pair<_U, _V>&& __x)
+	{
+	  construct(__p, piecewise_construct,
+		    std::forward_as_tuple(std::forward<_U>(__x.first)),
+		    std::forward_as_tuple(std::forward<_V>(__x.second)));
+	}
 
       template<typename _Tp>
         void destroy(_Tp* __p)
         {
-          auto& __outer = __outermost(*this);
-	  typedef typename std::decay<decltype(__outer)>::type __outer_type;
-          allocator_traits<__outer_type>::destroy(__outer, __p);
-        }
+	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
+	  _O_traits::destroy(__outermost(*this), __p);
+	}
 
       scoped_allocator_adaptor
       select_on_container_copy_construction() const
@@ -360,6 +411,29 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       friend bool
       operator==(const scoped_allocator_adaptor<_OutA1, _InA...>& __a,
                  const scoped_allocator_adaptor<_OutA2, _InA...>& __b) noexcept;
+
+    private:
+      template<typename _Tuple>
+	_Tuple&&
+	_M_construct_p(__uses_alloc0, _Tuple& __t)
+	{ return std::move(__t); }
+
+      template<typename... _Args>
+	std::tuple<allocator_arg_t, inner_allocator_type&, _Args...>
+	_M_construct_p(__uses_alloc1_, std::tuple<_Args...>& __t)
+	{
+	  typedef std::tuple<allocator_arg_t, inner_allocator_type&> _Tuple;
+	  return std::tuple_cat(_Tuple(allocator_arg, inner_allocator()),
+				std::move(__t));
+	}
+
+      template<typename... _Args>
+	std::tuple<_Args..., inner_allocator_type&>
+	_M_construct_p(__uses_alloc2_, std::tuple<_Args...>& __t)
+	{
+	  typedef std::tuple<inner_allocator_type&> _Tuple;
+	  return std::tuple_cat(std::move(__t), _Tuple(inner_allocator()));
+	}
     };
 
   template <typename _OutA1, typename _OutA2, typename... _InA>
diff --git a/libstdc++-v3/testsuite/20_util/scoped_allocator/2.cc b/libstdc++-v3/testsuite/20_util/scoped_allocator/2.cc
new file mode 100644
index 0000000..6bfa4d7
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/scoped_allocator/2.cc
@@ -0,0 +1,308 @@ 
+// { dg-options "-std=gnu++0x" }
+
+// Copyright (C) 2012 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library 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.
+
+// This library 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 library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <memory>
+#include <scoped_allocator>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+// 20.12.4 Scoped allocator adaptor members [allocator.adaptor.members]
+//
+// Test piecewise construction of std::pair by scoped_allocator_adaptor
+
+using __gnu_test::uneq_allocator;
+using std::scoped_allocator_adaptor;
+
+// a DefaultConstructible and CopyConstructible type
+struct def
+{
+  def() : id(999) { }
+
+  int id;
+};
+
+// a CopyConstructible and non-DefaultConstructible type
+struct copyable
+{
+  copyable(int id) : id(id) { }
+
+  // not constructed with an allocator so nothing to test
+  bool verify() const { return true; }
+
+  int id;
+};
+
+// a MoveConstructible and non-DefaultConstructible type
+struct move_only
+{
+  move_only(int id) : id(id) { }
+  move_only(move_only&&) = default;
+
+  // not constructed with an allocator so nothing to test
+  bool verify() const { return true; }
+
+  int id;
+};
+
+// a type for which std::uses_allocator is true
+struct uses_alloc_post
+{
+  typedef uneq_allocator<uses_alloc_post> allocator_type;
+
+  uses_alloc_post(const allocator_type& alloc)
+  : allocator_personality(alloc.get_personality()), id(999)
+  { }
+
+  uses_alloc_post(copyable arg, const allocator_type& alloc)
+  : allocator_personality(alloc.get_personality()), id(arg.id)
+  { }
+
+  uses_alloc_post(move_only arg, const allocator_type& alloc)
+  : allocator_personality(alloc.get_personality()), id(arg.id)
+  { }
+
+  // allocator-extended copy ctor
+  uses_alloc_post(const uses_alloc_post& other, const allocator_type& alloc)
+  : allocator_personality(alloc.get_personality()), id(other.id)
+  { }
+
+  // verify we were constructed with right allocator
+  bool verify() const { return allocator_personality == id; }
+
+  int allocator_personality;
+  int id;
+};
+
+// a type for which std::uses_allocator is true
+struct uses_alloc_pre : uses_alloc_post
+{
+  typedef uneq_allocator<uses_alloc_pre> allocator_type;
+
+  uses_alloc_pre(std::allocator_arg_t, const allocator_type& alloc)
+  : uses_alloc_post(alloc)
+  { }
+
+  uses_alloc_pre(std::allocator_arg_t, const allocator_type& alloc,
+                 copyable arg)
+  : uses_alloc_post(arg, alloc)
+  { }
+
+  // allocator-extended copy ctor
+  uses_alloc_pre(std::allocator_arg_t, const allocator_type& alloc,
+                 const uses_alloc_pre& other)
+  : uses_alloc_post(other, alloc)
+  { }
+
+  uses_alloc_pre(std::allocator_arg_t, const allocator_type& alloc,
+                 move_only arg)
+  : uses_alloc_post(std::move(arg), alloc)
+  { }
+};
+
+template<typename A, typename B>
+  void
+  test_def()
+  {
+    bool test __attribute((unused)) = false;
+
+    typedef std::pair<A, B> test_type;
+    typedef uneq_allocator<test_type> alloc_type;
+    typedef scoped_allocator_adaptor<alloc_type, alloc_type> alloc_adaptor;
+
+    int inner_id = 2;
+    alloc_adaptor a(-1, alloc_type(inner_id)); // outer=-1, inner=2
+
+    // all pair members that can be constructed with an allocator
+    // should be constructed with the inner allocator, with personality==2
+
+    auto p = a.allocate(1);
+
+    // construct(pair<T1, T2>* p, piecewise_construct_t, tuple<...>, tuple<...>)
+    std::tuple<> t;
+    a.construct(p, std::piecewise_construct, t, t);
+    VERIFY( p->first.id == 999 );
+    VERIFY( p->second.id == 999 );
+    a.destroy(p);
+
+    // construct(pair<T1, T2>* __p)
+    a.construct(p);
+    VERIFY( p->first.id == 999 );
+    VERIFY( p->second.id == 999 );
+    auto pp = *p;
+    a.destroy(p);
+
+    // construct(pair<T1, T2>* p, const pair<U, V>& x)
+    a.construct(p, pp);
+    VERIFY( p->first.id == 999 );
+    VERIFY( p->second.id == 999 );
+    a.destroy(p);
+
+    // construct(pair<T1, T2>* p, pair<U, V>&& x)
+    a.construct(p, std::move(pp));
+    VERIFY( p->first.id == 999 );
+    VERIFY( p->second.id == 999 );
+    a.destroy(p);
+
+    a.deallocate(p, 1);
+  }
+
+template<typename A, typename B>
+  void
+  test_copying()
+  {
+    bool test __attribute((unused)) = false;
+
+    typedef std::pair<A, B> test_type;
+    typedef uneq_allocator<test_type> alloc_type;
+    typedef scoped_allocator_adaptor<alloc_type, alloc_type> alloc_adaptor;
+
+    int inner_id = 2;
+    alloc_adaptor a(-1, alloc_type(inner_id)); // outer=-1, inner=2
+
+    // all pair members that can be constructed with an allocator
+    // should be constructed with the inner allocator, with personality==2
+
+    auto p = a.allocate(1);
+
+    // construct(pair<T1, T2>* p, piecewise_construct_t, tuple<...>, tuple<...>)
+    auto t = std::make_tuple(copyable(inner_id));
+    a.construct(p, std::piecewise_construct, t, t);
+    VERIFY( p->first.verify() );
+    VERIFY( p->second.verify() );
+    a.destroy(p);
+
+    // construct(pair<T1, T2>* __p)
+    // cannot test this overload using non-DefaultConstructible types
+
+    // construct(pair<T1, T2>* p, U&& x, V&& y)
+    copyable c(inner_id);
+    a.construct(p, c, c);
+    VERIFY( p->first.verify() );
+    VERIFY( p->second.verify() );
+    auto pp = *p;
+    a.destroy(p);
+
+    // construct(pair<T1, T2>* p, const pair<U, V>& x)
+    a.construct(p, pp);
+    VERIFY( p->first.verify() );
+    VERIFY( p->second.verify() );
+    a.destroy(p);
+
+    // construct(pair<T1, T2>* p, pair<U, V>&& x)
+    a.construct(p, std::move(pp));
+    VERIFY( p->first.verify() );
+    VERIFY( p->second.verify() );
+    a.destroy(p);
+
+    a.deallocate(p, 1);
+  }
+
+template<typename A, typename B>
+  void
+  test_moving()
+  {
+    bool test __attribute((unused)) = false;
+
+    typedef std::pair<A, B> test_type;
+    typedef uneq_allocator<test_type> alloc_type;
+    typedef scoped_allocator_adaptor<alloc_type, alloc_type> alloc_adaptor;
+
+    int inner_id = 2;
+    alloc_adaptor a(-1, alloc_type(inner_id)); // outer=-1, inner=2
+
+    // all pair members that can be constructed with an allocator
+    // should be constructed with the inner allocator, with personality==2
+
+    auto p = a.allocate(1);
+
+    // construct(pair<T1, T2>* p, piecewise_construct_t, tuple<...>, tuple<...>)
+    a.construct(p, std::piecewise_construct,
+                std::make_tuple(move_only(inner_id)),
+                std::make_tuple(move_only(inner_id)));
+    VERIFY( p->first.verify() );
+    VERIFY( p->second.verify() );
+    a.destroy(p);
+
+    // construct(pair<T1, T2>* __p)
+    // cannot test this overload using non-DefaultConstructible types
+
+    // construct(pair<T1, T2>* p, U&& x, V&& y)
+    a.construct(p, move_only(inner_id), move_only(inner_id));
+    VERIFY( p->first.verify() );
+    VERIFY( p->second.verify() );
+    a.destroy(p);
+
+    // construct(pair<T1, T2>* p, const pair<U, V>& x)
+    // cannot test this overload using move-only types
+
+    // construct(pair<T1, T2>* p, pair<U, V>&& x)
+    a.construct(p, std::make_pair(move_only(inner_id), move_only(inner_id)));
+    VERIFY( p->first.verify() );
+    VERIFY( p->second.verify() );
+    a.destroy(p);
+
+    a.deallocate(p, 1);
+  }
+
+void test01()
+{
+  test_def<def, def>();
+  test_def<def, uses_alloc_pre>();
+  test_def<def, uses_alloc_post>();
+  test_def<uses_alloc_pre, def>();
+  test_def<uses_alloc_pre, uses_alloc_pre>();
+  test_def<uses_alloc_pre, uses_alloc_post>();
+  test_def<uses_alloc_post, def>();
+  test_def<uses_alloc_post, uses_alloc_pre>();
+  test_def<uses_alloc_post, uses_alloc_post>();
+}
+
+void test02()
+{
+  test_copying<copyable, copyable>();
+  test_copying<copyable, uses_alloc_pre>();
+  test_copying<copyable, uses_alloc_post>();
+  test_copying<uses_alloc_pre, copyable>();
+  test_copying<uses_alloc_pre, uses_alloc_pre>();
+  test_copying<uses_alloc_pre, uses_alloc_post>();
+  test_copying<uses_alloc_post, copyable>();
+  test_copying<uses_alloc_post, uses_alloc_pre>();
+  test_copying<uses_alloc_post, uses_alloc_post>();
+}
+
+void test03()
+{
+  test_moving<move_only, move_only>();
+  test_moving<move_only, uses_alloc_pre>();
+  test_moving<move_only, uses_alloc_post>();
+  test_moving<uses_alloc_pre, move_only>();
+  test_moving<uses_alloc_pre, uses_alloc_pre>();
+  test_moving<uses_alloc_pre, uses_alloc_post>();
+  test_moving<uses_alloc_post, move_only>();
+  test_moving<uses_alloc_post, uses_alloc_pre>();
+  test_moving<uses_alloc_post, uses_alloc_post>();
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+}