Home | History | Annotate | Line # | Download | only in std
      1 // <scoped_allocator> -*- C++ -*-
      2 
      3 // Copyright (C) 2011-2022 Free Software Foundation, Inc.
      4 //
      5 // This file is part of the GNU ISO C++ Library.  This library is free
      6 // software; you can redistribute it and/or modify it under the
      7 // terms of the GNU General Public License as published by the
      8 // Free Software Foundation; either version 3, or (at your option)
      9 // any later version.
     10 
     11 // This library is distributed in the hope that it will be useful,
     12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 // GNU General Public License for more details.
     15 
     16 // Under Section 7 of GPL version 3, you are granted additional
     17 // permissions described in the GCC Runtime Library Exception, version
     18 // 3.1, as published by the Free Software Foundation.
     19 
     20 // You should have received a copy of the GNU General Public License and
     21 // a copy of the GCC Runtime Library Exception along with this program;
     22 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     23 // <http://www.gnu.org/licenses/>.
     24 
     25 /** @file include/scoped_allocator
     26  *  This is a Standard C++ Library header.
     27  */
     28 
     29 #ifndef _SCOPED_ALLOCATOR
     30 #define _SCOPED_ALLOCATOR 1
     31 
     32 #pragma GCC system_header
     33 
     34 #if __cplusplus < 201103L
     35 # include <bits/c++0x_warning.h>
     36 #else
     37 
     38 #include <tuple>
     39 #include <bits/alloc_traits.h>
     40 #include <bits/stl_pair.h>
     41 #include <bits/uses_allocator.h>
     42 #if __cplusplus > 201703L
     43 # include <bits/uses_allocator_args.h>
     44 #endif
     45 
     46 namespace std _GLIBCXX_VISIBILITY(default)
     47 {
     48 _GLIBCXX_BEGIN_NAMESPACE_VERSION
     49 
     50   /**
     51    * @addtogroup allocators
     52    * @{
     53    */
     54 
     55   template<typename _OuterAlloc, typename... _InnerAllocs>
     56     class scoped_allocator_adaptor;
     57 
     58   /// @cond undocumented
     59 
     60   template<typename _Alloc>
     61     using __outer_allocator_t
     62       = decltype(std::declval<_Alloc>().outer_allocator());
     63 
     64   template<typename _Alloc, typename = void>
     65     struct __outermost_type
     66     {
     67       using type = _Alloc;
     68       static type& _S_outermost(_Alloc& __a) noexcept { return __a; }
     69     };
     70 
     71   template<typename _Alloc>
     72     struct __outermost_type<_Alloc, __void_t<__outer_allocator_t<_Alloc>>>
     73     : __outermost_type<
     74       typename remove_reference<__outer_allocator_t<_Alloc>>::type
     75     >
     76     {
     77       using __base = __outermost_type<
     78         typename remove_reference<__outer_allocator_t<_Alloc>>::type
     79       >;
     80 
     81       static typename __base::type&
     82       _S_outermost(_Alloc& __a) noexcept
     83       { return __base::_S_outermost(__a.outer_allocator()); }
     84     };
     85 
     86   // Implementation of the OUTERMOST pseudofunction
     87   template<typename _Alloc>
     88     inline typename __outermost_type<_Alloc>::type&
     89     __outermost(_Alloc& __a)
     90     { return __outermost_type<_Alloc>::_S_outermost(__a); }
     91 
     92   template<typename...>
     93     struct __inner_type_impl;
     94 
     95   template<typename _Outer>
     96     struct __inner_type_impl<_Outer>
     97     {
     98       typedef scoped_allocator_adaptor<_Outer> __type;
     99 
    100       __inner_type_impl() = default;
    101       __inner_type_impl(const __inner_type_impl&) = default;
    102       __inner_type_impl(__inner_type_impl&&) = default;
    103       __inner_type_impl& operator=(const __inner_type_impl&) = default;
    104       __inner_type_impl& operator=(__inner_type_impl&&) = default;
    105 
    106       template<typename _Alloc>
    107       __inner_type_impl(const __inner_type_impl<_Alloc>& __other) noexcept
    108       { }
    109 
    110       template<typename _Alloc>
    111       __inner_type_impl(__inner_type_impl<_Alloc>&& __other) noexcept
    112       { }
    113 
    114       __type&
    115       _M_get(__type* __p) noexcept { return *__p; }
    116 
    117       const __type&
    118       _M_get(const __type* __p) const noexcept { return *__p; }
    119 
    120       tuple<>
    121       _M_tie() const noexcept { return tuple<>(); }
    122 
    123       bool
    124       operator==(const __inner_type_impl&) const noexcept
    125       { return true; }
    126     };
    127 
    128   template<typename _Outer, typename _InnerHead, typename... _InnerTail>
    129     struct __inner_type_impl<_Outer, _InnerHead, _InnerTail...>
    130     {
    131       typedef scoped_allocator_adaptor<_InnerHead, _InnerTail...> __type;
    132 
    133       __inner_type_impl() = default;
    134       __inner_type_impl(const __inner_type_impl&) = default;
    135       __inner_type_impl(__inner_type_impl&&) = default;
    136       __inner_type_impl& operator=(const __inner_type_impl&) = default;
    137       __inner_type_impl& operator=(__inner_type_impl&&) = default;
    138 
    139       template<typename... _Allocs>
    140       __inner_type_impl(const __inner_type_impl<_Allocs...>& __other) noexcept
    141       : _M_inner(__other._M_inner) { }
    142 
    143       template<typename... _Allocs>
    144       __inner_type_impl(__inner_type_impl<_Allocs...>&& __other) noexcept
    145       : _M_inner(std::move(__other._M_inner)) { }
    146 
    147     template<typename... _Args>
    148       explicit
    149       __inner_type_impl(_Args&&... __args) noexcept
    150       : _M_inner(std::forward<_Args>(__args)...) { }
    151 
    152       __type&
    153       _M_get(void*) noexcept { return _M_inner; }
    154 
    155       const __type&
    156       _M_get(const void*) const noexcept { return _M_inner; }
    157 
    158       tuple<const _InnerHead&, const _InnerTail&...>
    159       _M_tie() const noexcept
    160       { return _M_inner._M_tie(); }
    161 
    162       bool
    163       operator==(const __inner_type_impl& __other) const noexcept
    164       { return _M_inner == __other._M_inner; }
    165 
    166     private:
    167       template<typename...> friend struct __inner_type_impl;
    168       template<typename, typename...> friend class scoped_allocator_adaptor;
    169 
    170       __type _M_inner;
    171     };
    172 
    173   /// @endcond
    174 
    175   /// An adaptor to recursively pass an allocator to the objects it constructs
    176   template<typename _OuterAlloc, typename... _InnerAllocs>
    177     class scoped_allocator_adaptor
    178     : public _OuterAlloc
    179     {
    180       typedef allocator_traits<_OuterAlloc> __traits;
    181 
    182       typedef __inner_type_impl<_OuterAlloc, _InnerAllocs...> __inner_type;
    183       __inner_type _M_inner;
    184 
    185       template<typename _Outer, typename... _Inner>
    186         friend class scoped_allocator_adaptor;
    187 
    188       template<typename...>
    189 	friend struct __inner_type_impl;
    190 
    191       tuple<const _OuterAlloc&, const _InnerAllocs&...>
    192       _M_tie() const noexcept
    193       { return std::tuple_cat(std::tie(outer_allocator()), _M_inner._M_tie()); }
    194 
    195       template<typename _Alloc>
    196 	using __outermost_alloc_traits
    197 	  = allocator_traits<typename __outermost_type<_Alloc>::type>;
    198 
    199 #if ! __cpp_lib_make_obj_using_allocator
    200       template<typename _Tp, typename... _Args>
    201         void
    202         _M_construct(__uses_alloc0, _Tp* __p, _Args&&... __args)
    203         {
    204 	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
    205 	  _O_traits::construct(__outermost(*this), __p,
    206 			       std::forward<_Args>(__args)...);
    207         }
    208 
    209       typedef __uses_alloc1<typename __inner_type::__type> __uses_alloc1_;
    210       typedef __uses_alloc2<typename __inner_type::__type> __uses_alloc2_;
    211 
    212       template<typename _Tp, typename... _Args>
    213         void
    214         _M_construct(__uses_alloc1_, _Tp* __p, _Args&&... __args)
    215         {
    216 	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
    217 	  _O_traits::construct(__outermost(*this), __p,
    218 			       allocator_arg, inner_allocator(),
    219 			       std::forward<_Args>(__args)...);
    220         }
    221 
    222       template<typename _Tp, typename... _Args>
    223         void
    224         _M_construct(__uses_alloc2_, _Tp* __p, _Args&&... __args)
    225         {
    226 	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
    227 	  _O_traits::construct(__outermost(*this), __p,
    228 			       std::forward<_Args>(__args)...,
    229 			       inner_allocator());
    230         }
    231 #endif // ! make_obj_using_allocator
    232 
    233       template<typename _Alloc>
    234         static _Alloc
    235         _S_select_on_copy(const _Alloc& __a)
    236         {
    237           typedef allocator_traits<_Alloc> __a_traits;
    238           return __a_traits::select_on_container_copy_construction(__a);
    239         }
    240 
    241       template<std::size_t... _Indices>
    242         scoped_allocator_adaptor(tuple<const _OuterAlloc&,
    243                                        const _InnerAllocs&...> __refs,
    244                                  _Index_tuple<_Indices...>)
    245         : _OuterAlloc(_S_select_on_copy(std::get<0>(__refs))),
    246           _M_inner(_S_select_on_copy(std::get<_Indices+1>(__refs))...)
    247         { }
    248 
    249       // Used to constrain constructors to disallow invalid conversions.
    250       template<typename _Alloc>
    251         using _Constructible = typename enable_if<
    252             is_constructible<_OuterAlloc, _Alloc>::value
    253           >::type;
    254 
    255       // _GLIBCXX_RESOLVE_LIB_DEFECTS
    256       // 2975. Missing case for pair construction in scoped [...] allocators
    257       template<typename _Tp>
    258 	struct __not_pair { using type = void; };
    259 
    260       template<typename _Tp, typename _Up>
    261 	struct __not_pair<pair<_Tp, _Up>> { };
    262 
    263     public:
    264       typedef _OuterAlloc                       outer_allocator_type;
    265       typedef typename __inner_type::__type     inner_allocator_type;
    266 
    267       typedef typename __traits::value_type             value_type;
    268       typedef typename __traits::size_type              size_type;
    269       typedef typename __traits::difference_type        difference_type;
    270       typedef typename __traits::pointer                pointer;
    271       typedef typename __traits::const_pointer          const_pointer;
    272       typedef typename __traits::void_pointer           void_pointer;
    273       typedef typename __traits::const_void_pointer     const_void_pointer;
    274 
    275       typedef typename __or_<
    276 	typename __traits::propagate_on_container_copy_assignment,
    277 	typename allocator_traits<_InnerAllocs>::
    278 	  propagate_on_container_copy_assignment...>::type
    279 	  propagate_on_container_copy_assignment;
    280 
    281       typedef typename __or_<
    282 	typename __traits::propagate_on_container_move_assignment,
    283 	typename allocator_traits<_InnerAllocs>::
    284 	  propagate_on_container_move_assignment...>::type
    285 	  propagate_on_container_move_assignment;
    286 
    287       typedef typename __or_<
    288 	typename __traits::propagate_on_container_swap,
    289 	typename allocator_traits<_InnerAllocs>::
    290 	  propagate_on_container_swap...>::type
    291 	  propagate_on_container_swap;
    292 
    293       typedef typename __and_<
    294 	typename __traits::is_always_equal,
    295 	typename allocator_traits<_InnerAllocs>::is_always_equal...>::type
    296 	  is_always_equal;
    297 
    298       template <class _Tp>
    299         struct rebind
    300         {
    301           typedef scoped_allocator_adaptor<
    302             typename __traits::template rebind_alloc<_Tp>,
    303             _InnerAllocs...> other;
    304         };
    305 
    306       scoped_allocator_adaptor() : _OuterAlloc(), _M_inner() { }
    307 
    308       template<typename _Outer2, typename = _Constructible<_Outer2>>
    309         scoped_allocator_adaptor(_Outer2&& __outer,
    310                                  const _InnerAllocs&... __inner) noexcept
    311         : _OuterAlloc(std::forward<_Outer2>(__outer)),
    312           _M_inner(__inner...)
    313         { }
    314 
    315       scoped_allocator_adaptor(const scoped_allocator_adaptor& __other) noexcept
    316       : _OuterAlloc(__other.outer_allocator()),
    317 	_M_inner(__other._M_inner)
    318       { }
    319 
    320       scoped_allocator_adaptor(scoped_allocator_adaptor&& __other) noexcept
    321       : _OuterAlloc(std::move(__other.outer_allocator())),
    322 	_M_inner(std::move(__other._M_inner))
    323       { }
    324 
    325       template<typename _Outer2, typename = _Constructible<const _Outer2&>>
    326         scoped_allocator_adaptor(
    327 	  const scoped_allocator_adaptor<_Outer2, _InnerAllocs...>& __other
    328 	) noexcept
    329         : _OuterAlloc(__other.outer_allocator()),
    330           _M_inner(__other._M_inner)
    331         { }
    332 
    333       template<typename _Outer2, typename = _Constructible<_Outer2>>
    334         scoped_allocator_adaptor(
    335 	  scoped_allocator_adaptor<_Outer2, _InnerAllocs...>&& __other) noexcept
    336         : _OuterAlloc(std::move(__other.outer_allocator())),
    337           _M_inner(std::move(__other._M_inner))
    338         { }
    339 
    340       scoped_allocator_adaptor&
    341       operator=(const scoped_allocator_adaptor&) = default;
    342 
    343       scoped_allocator_adaptor&
    344       operator=(scoped_allocator_adaptor&&) = default;
    345 
    346       inner_allocator_type&
    347       inner_allocator() noexcept
    348       { return _M_inner._M_get(this); }
    349 
    350       const inner_allocator_type&
    351       inner_allocator() const noexcept
    352       { return _M_inner._M_get(this); }
    353 
    354       outer_allocator_type&
    355       outer_allocator() noexcept
    356       { return static_cast<_OuterAlloc&>(*this); }
    357 
    358       const outer_allocator_type&
    359       outer_allocator() const noexcept
    360       { return static_cast<const _OuterAlloc&>(*this); }
    361 
    362       _GLIBCXX_NODISCARD pointer
    363       allocate(size_type __n)
    364       { return __traits::allocate(outer_allocator(), __n); }
    365 
    366       _GLIBCXX_NODISCARD pointer
    367       allocate(size_type __n, const_void_pointer __hint)
    368       { return __traits::allocate(outer_allocator(), __n, __hint); }
    369 
    370       void deallocate(pointer __p, size_type __n) noexcept
    371       { return __traits::deallocate(outer_allocator(), __p, __n); }
    372 
    373       size_type max_size() const
    374       { return __traits::max_size(outer_allocator()); }
    375 
    376 #if ! __cpp_lib_make_obj_using_allocator
    377       template<typename _Tp, typename... _Args>
    378 	typename __not_pair<_Tp>::type
    379 	construct(_Tp* __p, _Args&&... __args)
    380 	{
    381 	  auto& __inner = inner_allocator();
    382 	  auto __use_tag
    383 	    = std::__use_alloc<_Tp, inner_allocator_type, _Args...>(__inner);
    384 	  _M_construct(__use_tag, __p, std::forward<_Args>(__args)...);
    385 	}
    386 
    387       template<typename _T1, typename _T2, typename... _Args1,
    388 	       typename... _Args2>
    389 	void
    390 	construct(pair<_T1, _T2>* __p, piecewise_construct_t,
    391 		  tuple<_Args1...> __x, tuple<_Args2...> __y)
    392 	{
    393 	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
    394 	  // 2203.  wrong argument types for piecewise construction
    395 	  auto& __inner = inner_allocator();
    396 	  auto __x_use_tag
    397 	    = std::__use_alloc<_T1, inner_allocator_type, _Args1...>(__inner);
    398 	  auto __y_use_tag
    399 	    = std::__use_alloc<_T2, inner_allocator_type, _Args2...>(__inner);
    400 	  typename _Build_index_tuple<sizeof...(_Args1)>::__type __x_indices;
    401 	  typename _Build_index_tuple<sizeof...(_Args2)>::__type __y_indices;
    402 	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
    403 	  _O_traits::construct(__outermost(*this), __p, piecewise_construct,
    404 			       _M_construct_p(__x_use_tag, __x_indices, __x),
    405 			       _M_construct_p(__y_use_tag, __y_indices, __y));
    406 	}
    407 
    408       template<typename _T1, typename _T2>
    409 	void
    410 	construct(pair<_T1, _T2>* __p)
    411 	{ construct(__p, piecewise_construct, tuple<>(), tuple<>()); }
    412 
    413       template<typename _T1, typename _T2, typename _Up, typename _Vp>
    414 	void
    415 	construct(pair<_T1, _T2>* __p, _Up&& __u, _Vp&& __v)
    416 	{
    417 	  construct(__p, piecewise_construct,
    418 		    std::forward_as_tuple(std::forward<_Up>(__u)),
    419 		    std::forward_as_tuple(std::forward<_Vp>(__v)));
    420 	}
    421 
    422       template<typename _T1, typename _T2, typename _Up, typename _Vp>
    423 	void
    424 	construct(pair<_T1, _T2>* __p, const pair<_Up, _Vp>& __x)
    425 	{
    426 	  construct(__p, piecewise_construct,
    427 		    std::forward_as_tuple(__x.first),
    428 		    std::forward_as_tuple(__x.second));
    429 	}
    430 
    431       template<typename _T1, typename _T2, typename _Up, typename _Vp>
    432 	void
    433 	construct(pair<_T1, _T2>* __p, pair<_Up, _Vp>&& __x)
    434 	{
    435 	  construct(__p, piecewise_construct,
    436 		    std::forward_as_tuple(std::forward<_Up>(__x.first)),
    437 		    std::forward_as_tuple(std::forward<_Vp>(__x.second)));
    438 	}
    439 #else // make_obj_using_allocator
    440       template<typename _Tp, typename... _Args>
    441 	__attribute__((__nonnull__))
    442 	void
    443 	construct(_Tp* __p, _Args&&... __args)
    444 	{
    445 	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
    446 	  std::apply([__p, this](auto&&... __newargs) {
    447 	      _O_traits::construct(__outermost(*this), __p,
    448 		  std::forward<decltype(__newargs)>(__newargs)...);
    449 	  },
    450 	  uses_allocator_construction_args<_Tp>(inner_allocator(),
    451 	    std::forward<_Args>(__args)...));
    452 	}
    453 #endif
    454 
    455       template<typename _Tp>
    456         void destroy(_Tp* __p)
    457         {
    458 	  typedef __outermost_alloc_traits<scoped_allocator_adaptor> _O_traits;
    459 	  _O_traits::destroy(__outermost(*this), __p);
    460 	}
    461 
    462       scoped_allocator_adaptor
    463       select_on_container_copy_construction() const
    464       {
    465         typedef typename _Build_index_tuple<sizeof...(_InnerAllocs)>::__type
    466 	    _Indices;
    467         return scoped_allocator_adaptor(_M_tie(), _Indices());
    468       }
    469 
    470       template <typename _OutA1, typename _OutA2, typename... _InA>
    471       friend bool
    472       operator==(const scoped_allocator_adaptor<_OutA1, _InA...>& __a,
    473                  const scoped_allocator_adaptor<_OutA2, _InA...>& __b) noexcept;
    474 
    475     private:
    476 #if ! __cpp_lib_make_obj_using_allocator
    477       template<typename _Ind, typename... _Args>
    478 	tuple<_Args&&...>
    479 	_M_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t)
    480 	{ return std::move(__t); }
    481 
    482       template<size_t... _Ind, typename... _Args>
    483 	tuple<allocator_arg_t, inner_allocator_type&, _Args&&...>
    484 	_M_construct_p(__uses_alloc1_, _Index_tuple<_Ind...>,
    485 		       tuple<_Args...>& __t)
    486 	{
    487 	  return { allocator_arg, inner_allocator(),
    488 	      std::get<_Ind>(std::move(__t))...
    489 	  };
    490 	}
    491 
    492       template<size_t... _Ind, typename... _Args>
    493 	tuple<_Args&&..., inner_allocator_type&>
    494 	_M_construct_p(__uses_alloc2_, _Index_tuple<_Ind...>,
    495 		       tuple<_Args...>& __t)
    496 	{
    497 	  return { std::get<_Ind>(std::move(__t))..., inner_allocator() };
    498 	}
    499 #endif // ! make_obj_using_allocator
    500     };
    501 
    502   /// @related std::scoped_allocator_adaptor
    503   template <typename _OutA1, typename _OutA2, typename... _InA>
    504     inline bool
    505     operator==(const scoped_allocator_adaptor<_OutA1, _InA...>& __a,
    506                const scoped_allocator_adaptor<_OutA2, _InA...>& __b) noexcept
    507     {
    508       return __a.outer_allocator() == __b.outer_allocator()
    509           && __a._M_inner == __b._M_inner;
    510     }
    511 
    512 #if __cpp_impl_three_way_comparison < 201907L
    513   /// @related std::scoped_allocator_adaptor
    514   template <typename _OutA1, typename _OutA2, typename... _InA>
    515     inline bool
    516     operator!=(const scoped_allocator_adaptor<_OutA1, _InA...>& __a,
    517                const scoped_allocator_adaptor<_OutA2, _InA...>& __b) noexcept
    518     { return !(__a == __b); }
    519 #endif
    520 
    521   /// @}
    522 
    523 _GLIBCXX_END_NAMESPACE_VERSION
    524 } // namespace
    525 
    526 #endif // C++11
    527 
    528 #endif // _SCOPED_ALLOCATOR
    529