xref: /netbsd-src/external/gpl3/gcc/dist/libstdc++-v3/include/std/scoped_allocator (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
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
46namespace 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