Make std::scoped_allocator_adaptor's OUTERMOST recursive
authorJonathan Wakely <jwakely@redhat.com>
Thu, 6 Oct 2016 21:22:35 +0000 (22:22 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 6 Oct 2016 21:22:35 +0000 (22:22 +0100)
* doc/xml/manual/status_cxx2011.xml: Update status.
* include/std/scoped_allocator (__outer_allocator_t, __outermost_type):
New helpers for recursive OUTERMOST.
(__outermost): Use __outermost_type::_S_outermost.
(__do_outermost, scoped_allocator_adaptor::__outermost_type): Remove.
(scoped_allocator_adaptor::__outermost_alloc_traits): Use new
__outermost_type helper.
* testsuite/20_util/scoped_allocator/outermost.cc: New test.

From-SVN: r240844

libstdc++-v3/ChangeLog
libstdc++-v3/doc/xml/manual/status_cxx2011.xml
libstdc++-v3/include/std/scoped_allocator
libstdc++-v3/testsuite/20_util/scoped_allocator/3.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/scoped_allocator/outermost.cc [new file with mode: 0644]

index b3116fc785c804c7ae680265ca75df4490c6d8f1..d9fa88af6e9e07d7f6d01a02ce279c5befb8eb8e 100644 (file)
@@ -1,3 +1,18 @@
+2016-10-06  Jonathan Wakely  <jwakely@redhat.com>
+
+       * doc/xml/manual/status_cxx2011.xml: Update status.
+       * include/std/scoped_allocator (__outer_allocator_t, __outermost_type):
+       New helpers for recursive OUTERMOST.
+       (__outermost): Use __outermost_type::_S_outermost.
+       (__do_outermost, scoped_allocator_adaptor::__outermost_type): Remove.
+       (scoped_allocator_adaptor::__outermost_alloc_traits): Use new
+       __outermost_type helper.
+       (scoped_allocator_adaptor::_Constructible): New alias template.
+       (scoped_allocator_adaptor::scoped_allocator_adaptor<_Outer2>):
+       Constrain template constructors.
+       * testsuite/20_util/scoped_allocator/3.cc: New test.
+       * testsuite/20_util/scoped_allocator/outermost.cc: New test.
+
 2016-10-05  Jonathan Wakely  <jwakely@redhat.com>
 
        PR libstdc++/70564
index cf99956b7eb7c2ea2460d20f5d7cbfee9b31592c..e1b372d643fb2abe7416b044501596c045f06df2 100644 (file)
@@ -1007,11 +1007,10 @@ particular release.
       <entry/>
     </row>
     <row>
-      <?dbhtml bgcolor="#B0B0B0" ?>
       <entry>20.12.4</entry>
       <entry>Scoped allocator adaptor members</entry>
-      <entry>Partial</entry>
-      <entry>OUTERMOST is not recursive.</entry>
+      <entry>Y</entry>
+      <entry/>
     </row>
     <row>
       <entry>20.12.5</entry>
index 310c85c65073e5c93cb11f033960b13a686208a0..39762fe444d4f8c46b2db5f1da493b5b29e5e6c6 100644 (file)
@@ -49,21 +49,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    */
 
   template<typename _Alloc>
-    inline auto
-    __do_outermost(_Alloc& __a, int) -> decltype(__a.outer_allocator())
-    { return __a.outer_allocator(); }
+    using __outer_allocator_t
+      = decltype(std::declval<_Alloc>().outer_allocator());
+
+  template<typename _Alloc, typename = void>
+    struct __outermost_type
+    {
+      using type = _Alloc;
+      static type& _S_outermost(_Alloc& __a) { return __a; }
+    };
 
   template<typename _Alloc>
-    inline _Alloc&
-    __do_outermost(_Alloc& __a, ...)
-    { return __a; }
+    struct __outermost_type<_Alloc, __void_t<__outer_allocator_t<_Alloc>>>
+    : __outermost_type<
+      typename remove_reference<__outer_allocator_t<_Alloc>>::type
+    >
+    {
+      using __base = __outermost_type<
+        typename remove_reference<__outer_allocator_t<_Alloc>>::type
+      >;
+
+      static typename __base::type&
+      _S_outermost(_Alloc& __a)
+      { return __base::_S_outermost(__a.outer_allocator()); }
+    };
 
-  // TODO: make recursive (see note in 20.12.4/1)
   template<typename _Alloc>
-    inline auto
+    inline typename __outermost_type<_Alloc>::type&
     __outermost(_Alloc& __a)
-    -> decltype(__do_outermost(__a, 0))
-    { return __do_outermost(__a, 0); }
+    { return __outermost_type<_Alloc>::_S_outermost(__a); }
 
   template<typename _OuterAlloc, typename... _InnerAllocs>
     class scoped_allocator_adaptor;
@@ -169,13 +183,9 @@ _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>>;
+         = allocator_traits<typename __outermost_type<_Alloc>::type>;
 
       template<typename _Tp, typename... _Args>
         void
@@ -225,6 +235,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
           _M_inner(_S_select_on_copy(std::get<_Indices+1>(__refs))...)
         { }
 
+      // Used to constrain constructors to disallow invalid conversions.
+      template<typename _Alloc>
+        using _Constructible = typename enable_if<
+            is_constructible<_OuterAlloc, _Alloc>::value
+          >::type;
+
     public:
       typedef _OuterAlloc                       outer_allocator_type;
       typedef typename __inner_type::__type     inner_allocator_type;
@@ -270,7 +286,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       scoped_allocator_adaptor() : _OuterAlloc(), _M_inner() { }
 
-      template<typename _Outer2>
+      template<typename _Outer2, typename = _Constructible<_Outer2>>
         scoped_allocator_adaptor(_Outer2&& __outer,
                                  const _InnerAllocs&... __inner)
         : _OuterAlloc(std::forward<_Outer2>(__outer)),
@@ -287,14 +303,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        _M_inner(std::move(__other._M_inner))
       { }
 
-      template<typename _Outer2>
+      template<typename _Outer2, typename = _Constructible<const _Outer2&>>
         scoped_allocator_adaptor(
             const scoped_allocator_adaptor<_Outer2, _InnerAllocs...>& __other)
         : _OuterAlloc(__other.outer_allocator()),
           _M_inner(__other._M_inner)
         { }
 
-      template<typename _Outer2>
+      template<typename _Outer2, typename = _Constructible<_Outer2>>
         scoped_allocator_adaptor(
             scoped_allocator_adaptor<_Outer2, _InnerAllocs...>&& __other)
         : _OuterAlloc(std::move(__other.outer_allocator())),
diff --git a/libstdc++-v3/testsuite/20_util/scoped_allocator/3.cc b/libstdc++-v3/testsuite/20_util/scoped_allocator/3.cc
new file mode 100644 (file)
index 0000000..2461220
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright (C) 2016 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/>.
+
+// { dg-do compile { target c++11 } }
+
+#include <scoped_allocator>
+#include <testsuite_allocator.h>
+
+template<typename T>
+struct alloc
+{
+  using value_type = T;
+  alloc() = default;
+  template<typename U>
+    alloc(alloc<U>) { }
+  T* allocate(std::size_t);
+  void deallocate(T*, std::size_t);
+};
+
+template<typename T, typename U>
+  bool operator==(alloc<T>, alloc<U>) { return true; }
+
+template<typename T, typename U>
+  bool operator!=(alloc<T>, alloc<U>) { return false; }
+
+using scoped = std::scoped_allocator_adaptor<alloc<int>>;
+using other_alloc = __gnu_test::SimpleAllocator<int>;
+using other_scoped = std::scoped_allocator_adaptor<other_alloc>;
+
+using std::is_constructible;
+
+static_assert( is_constructible<scoped, const scoped&>::value,
+    "is_constructible<scoped, const scoped&>");
+static_assert( is_constructible<scoped, scoped>::value,
+    "is_constructible<scoped, scoped>");
+static_assert( is_constructible<scoped, const alloc<int>&>::value,
+    "is_constructible<scoped, const outer_allocator_type&>");
+static_assert( is_constructible<scoped, alloc<int>>::value,
+    "is_constructible<scoped, outer_allocator_type>");
+static_assert( is_constructible<scoped, const alloc<long>&>::value,
+    "is_constructible<scoped, const outer_allocator_type::rebind<U>::type&>");
+static_assert( is_constructible<scoped, alloc<long>>::value,
+    "is_constructible<scoped, outer_allocator_type::rebind<U>::type>");
+
+static_assert( !is_constructible<scoped, const other_alloc&>::value,
+    "!is_constructible<scoped, const other_alloc&>");
+static_assert( !is_constructible<scoped, other_alloc>::value,
+    "!is_constructible<scoped, other_alloc>");
+static_assert( !is_constructible<scoped, const other_scoped&>::value,
+    "!is_constructible<scoped, const other_scoped&>");
+static_assert( !is_constructible<scoped, other_scoped>::value,
+    "!is_constructible<scoped, other_scoped>");
diff --git a/libstdc++-v3/testsuite/20_util/scoped_allocator/outermost.cc b/libstdc++-v3/testsuite/20_util/scoped_allocator/outermost.cc
new file mode 100644 (file)
index 0000000..af31348
--- /dev/null
@@ -0,0 +1,91 @@
+// Copyright (C) 2016 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/>.
+
+// { dg-do compile { target c++11 } }
+
+#include <scoped_allocator>
+
+template<typename T>
+struct alloc
+{
+  using value_type = T;
+  alloc() = default;
+  template<typename U>
+    alloc(alloc<U>) { }
+  T* allocate(std::size_t);
+  void deallocate(T*, std::size_t);
+};
+
+template<typename T, typename U>
+  bool operator==(alloc<T>, alloc<U>) { return true; }
+
+template<typename T, typename U>
+  bool operator!=(alloc<T>, alloc<U>) { return false; }
+
+struct X
+{
+  using allocator_type = alloc<int>;
+  X(const allocator_type&);
+};
+
+template<typename A>
+struct nested_alloc : A
+{
+  nested_alloc() = default;
+  template<typename U>
+    nested_alloc(nested_alloc<U>) { }
+
+  A& outer_allocator() { return *this; }
+
+  template<typename U, typename... Args>
+    void construct(U*, Args&&...)
+    {
+      static_assert(!std::is_same<U, X>::value,
+          "OUTERMOST should recurse and use alloc<int> to construct X");
+    }
+};
+
+template<typename T, typename U>
+  bool operator==(nested_alloc<T> l, nested_alloc<U> r)
+  { return l.outer_allocator() == r.outer_allocator(); }
+
+template<typename T, typename U>
+  bool operator!=(nested_alloc<T> l, nested_alloc<U> r)
+  { return !(l == r); }
+
+template<typename A>
+  using scoped_alloc = std::scoped_allocator_adaptor<A>;
+
+void
+test01()
+{
+  scoped_alloc<nested_alloc<alloc<int>>> a;
+  alignas(X) char buf[sizeof(X)];
+  X* p = (X*)buf;
+  // Test that OUTERMOST is recursive and doesn't just unwrap one level:
+  a.construct(p);
+}
+
+void
+test02()
+{
+  scoped_alloc<scoped_alloc<nested_alloc<alloc<int>>>> a;
+  alignas(X) char buf[sizeof(X)];
+  X* p = (X*)buf;
+  // Test that OUTERMOST is recursive and doesn't just unwrap one level:
+  a.construct(p);
+}