|
| 1 | +// -*- C++ -*- |
| 2 | +//===----------------------------------------------------------------------===// |
| 3 | +// |
| 4 | +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 5 | +// See https://llvm.org/LICENSE.txt for license information. |
| 6 | +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 7 | +// |
| 8 | +//===----------------------------------------------------------------------===// |
| 9 | + |
| 10 | +#ifndef _LIBCPP___MEMORY_INDIRECT_H |
| 11 | +#define _LIBCPP___MEMORY_INDIRECT_H |
| 12 | + |
| 13 | +#include <__config> |
| 14 | + |
| 15 | +#include <__compare/strong_order.h> |
| 16 | +#include <__compare/synth_three_way.h> |
| 17 | +#include <__functional/hash.h> |
| 18 | +#include <__fwd/memory_resource.h> |
| 19 | +#include <__memory/addressof.h> |
| 20 | +#include <__memory/allocation_guard.h> |
| 21 | +#include <__memory/allocator_arg_t.h> |
| 22 | +#include <__memory/allocator_traits.h> |
| 23 | +#include <__memory/swap_allocator.h> |
| 24 | +#include <__type_traits/is_array.h> |
| 25 | +#include <__type_traits/is_object.h> |
| 26 | +#include <__type_traits/is_same.h> |
| 27 | +#include <__type_traits/remove_cv.h> |
| 28 | +#include <__utility/exchange.h> |
| 29 | +#include <__utility/forward.h> |
| 30 | +#include <__utility/in_place.h> |
| 31 | +#include <__utility/move.h> |
| 32 | +#include <__utility/swap.h> |
| 33 | +#include <initializer_list> |
| 34 | +#include <type_traits> |
| 35 | + |
| 36 | +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) |
| 37 | +# pragma GCC system_header |
| 38 | +#endif |
| 39 | + |
| 40 | +#if _LIBCPP_STD_VER >= 26 |
| 41 | + |
| 42 | +_LIBCPP_BEGIN_NAMESPACE_STD |
| 43 | + |
| 44 | +template <class _Tp, class _Allocator = allocator<_Tp>> |
| 45 | +class _LIBCPP_NO_SPECIALIZATIONS indirect { |
| 46 | +public: |
| 47 | + using value_type = _Tp; |
| 48 | + using allocator_type = _Allocator; |
| 49 | + using pointer = allocator_traits<_Allocator>::pointer; |
| 50 | + using const_pointer = allocator_traits<_Allocator>::const_pointer; |
| 51 | + |
| 52 | + static_assert(__check_valid_allocator<allocator_type>::value); |
| 53 | + static_assert(is_same_v<typename allocator_type::value_type, value_type>); |
| 54 | + static_assert(is_object_v<value_type>); |
| 55 | + static_assert(!is_array_v<value_type>); |
| 56 | + static_assert(!is_same_v<value_type, in_place_t>); |
| 57 | + static_assert(!__is_inplace_type<value_type>::value); |
| 58 | + static_assert(std::is_same_v<value_type, remove_cv_t<value_type>>, |
| 59 | + "value_type must not be const or volatile qualified"); |
| 60 | + |
| 61 | + // [indirect.ctor], constructors |
| 62 | + _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect() |
| 63 | + requires is_default_constructible_v<_Allocator> |
| 64 | + : __p_(__allocate_owned_object(__alloc_)) {} |
| 65 | + |
| 66 | + _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a) |
| 67 | + : __alloc_(__a), __p_(__allocate_owned_object(__alloc_)) {} |
| 68 | + |
| 69 | + _LIBCPP_HIDE_FROM_ABI constexpr indirect(const indirect& __other) |
| 70 | + : __alloc_(allocator_traits<_Allocator>::select_on_container_copy_construction(__other.__alloc_)), |
| 71 | + __p_(__other.valueless_after_move() ? nullptr : __allocate_owned_object(__alloc_, *__other)) {} |
| 72 | + |
| 73 | + _LIBCPP_HIDE_FROM_ABI constexpr indirect(allocator_arg_t, const _Allocator& __a, const indirect& __other) |
| 74 | + : __alloc_(__a), __p_(__other.valueless_after_move() ? nullptr : __allocate_owned_object(__alloc_, *__other)) {} |
| 75 | + |
| 76 | + _LIBCPP_HIDE_FROM_ABI constexpr indirect(indirect&& __other) noexcept |
| 77 | + : __alloc_(std::move(__other.__alloc_)), __p_(std::exchange(__other.__p_, nullptr)) {} |
| 78 | + |
| 79 | + _LIBCPP_HIDE_FROM_ABI constexpr indirect(allocator_arg_t, const _Allocator& __a, indirect&& __other) noexcept |
| 80 | + requires allocator_traits<_Allocator>::is_always_equal::value |
| 81 | + : __alloc_(__a), __p_(std::exchange(__other.__p_, nullptr)) {} |
| 82 | + |
| 83 | + _LIBCPP_HIDE_FROM_ABI constexpr indirect(allocator_arg_t, const _Allocator& __a, indirect&& __other) : __alloc_(__a) { |
| 84 | + if (__other.valueless_after_move()) { |
| 85 | + __p_ = nullptr; |
| 86 | + } else if (__alloc_ == __other.__alloc_) { |
| 87 | + __p_ = std::exchange(__other.__p_, nullptr); |
| 88 | + } else { |
| 89 | + __p_ = __allocate_owned_object(__alloc_, *std::move(__other)); |
| 90 | + __other.__destroy_owned_object(); |
| 91 | + __other.__p_ = nullptr; |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + template <class _U = _Tp> |
| 96 | + requires(!is_same_v<remove_cvref_t<_U>, indirect> && !is_same_v<remove_cvref_t<_U>, in_place_t> && |
| 97 | + is_constructible_v<_Tp, _U> && is_default_constructible_v<_Allocator>) |
| 98 | + _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(_U&& __u) |
| 99 | + : __p_(__allocate_owned_object(__alloc_, std::forward<_U>(__u))) {} |
| 100 | + |
| 101 | + template <class _U = _Tp> |
| 102 | + requires(!is_same_v<remove_cvref_t<_U>, indirect> && !is_same_v<remove_cvref_t<_U>, in_place_t> && |
| 103 | + is_constructible_v<_Tp, _U>) |
| 104 | + _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a, _U&& __u) |
| 105 | + : __alloc_(__a), __p_(__allocate_owned_object(__alloc_, std::forward<_U>(__u))) {} |
| 106 | + |
| 107 | + template <class... _Us> |
| 108 | + requires(is_constructible_v<_Tp, _Us...> && is_default_constructible_v<_Allocator>) |
| 109 | + _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(in_place_t, _Us&&... __us) |
| 110 | + : __p_(__allocate_owned_object(__alloc_, std::forward<_Us>(__us)...)) {} |
| 111 | + |
| 112 | + template <class... _Us> |
| 113 | + requires is_constructible_v<_Tp, _Us...> |
| 114 | + _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(allocator_arg_t, const _Allocator& __a, in_place_t, _Us&&... __us) |
| 115 | + : __alloc_(__a), __p_(__allocate_owned_object(__alloc_, std::forward<_Us>(__us)...)) {} |
| 116 | + |
| 117 | + template <class _I, class... _Us> |
| 118 | + requires(is_constructible_v<_Tp, initializer_list<_I>&, _Us...> && is_default_constructible_v<_Allocator>) |
| 119 | + _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect(in_place_t, initializer_list<_I> __ilist, _Us&&... __us) |
| 120 | + : __p_(__allocate_owned_object(__alloc_, __ilist, std::forward<_Us>(__us)...)) {} |
| 121 | + |
| 122 | + template <class _I, class... _Us> |
| 123 | + requires is_constructible_v<_Tp, initializer_list<_I>&, _Us...> |
| 124 | + _LIBCPP_HIDE_FROM_ABI constexpr explicit indirect( |
| 125 | + allocator_arg_t, const _Allocator& __a, in_place_t, initializer_list<_I> __ilist, _Us&&... __us) |
| 126 | + : __alloc_(__a), __p_(__allocate_owned_object(__alloc_, __ilist, std::forward<_Us>(__us)...)) {} |
| 127 | + |
| 128 | + // [indirect.dtor], destructor |
| 129 | + _LIBCPP_HIDE_FROM_ABI constexpr ~indirect() { __destroy_owned_object(); } |
| 130 | + |
| 131 | + // [indirect.assign], assignment |
| 132 | + _LIBCPP_HIDE_FROM_ABI constexpr indirect& operator=(const indirect& __other) { |
| 133 | + if (std::addressof(__other) == this) |
| 134 | + return *this; |
| 135 | + |
| 136 | + static constexpr bool __propagate_allocator = |
| 137 | + allocator_traits<_Allocator>::propagate_on_container_copy_assignment::value; |
| 138 | + if (__other.valueless_after_move()) { |
| 139 | + __destroy_owned_object(); |
| 140 | + __p_ = nullptr; |
| 141 | + } else if (!valueless_after_move() && __alloc_ == __other.__alloc_) { |
| 142 | + *__p_ = *__other; |
| 143 | + } else { |
| 144 | + pointer __new_p; |
| 145 | + if constexpr (__propagate_allocator) { |
| 146 | + // We need a mutable instance of the allocator, so make a copy. |
| 147 | + _Allocator __alloc_copy = __other.__alloc_; |
| 148 | + __new_p = __allocate_owned_object(__alloc_copy, *__other); |
| 149 | + } else { |
| 150 | + __new_p = __allocate_owned_object(__alloc_, *__other); |
| 151 | + } |
| 152 | + __destroy_owned_object(); |
| 153 | + __p_ = __new_p; |
| 154 | + } |
| 155 | + |
| 156 | + if constexpr (__propagate_allocator) |
| 157 | + __alloc_ = __other.__alloc_; |
| 158 | + |
| 159 | + return *this; |
| 160 | + } |
| 161 | + |
| 162 | + _LIBCPP_HIDE_FROM_ABI constexpr indirect& operator=(indirect&& __other) noexcept( |
| 163 | + allocator_traits<_Allocator>::propagate_on_container_move_assignment::value || |
| 164 | + allocator_traits<_Allocator>::is_always_equal::value) { |
| 165 | + if (std::addressof(__other) == this) |
| 166 | + return *this; |
| 167 | + |
| 168 | + static constexpr bool __propagate_allocator = |
| 169 | + allocator_traits<_Allocator>::propagate_on_container_move_assignment::value; |
| 170 | + |
| 171 | + pointer __new_p; |
| 172 | + if constexpr (__propagate_allocator || allocator_traits<_Allocator>::is_always_equal::value) { |
| 173 | + __new_p = __other.__p_; |
| 174 | + } else if (__other.valueless_after_move()) { |
| 175 | + __new_p = nullptr; |
| 176 | + } else if (__alloc_ == __other.__alloc_) { |
| 177 | + __new_p = __other.__p_; |
| 178 | + } else { |
| 179 | + __new_p = __allocate_owned_object(__alloc_, *std::move(__other)); |
| 180 | + __other.__destroy_owned_object(); |
| 181 | + } |
| 182 | + __other.__p_ = nullptr; |
| 183 | + __destroy_owned_object(); |
| 184 | + __p_ = __new_p; |
| 185 | + |
| 186 | + if constexpr (__propagate_allocator) |
| 187 | + __alloc_ = __other.__alloc_; |
| 188 | + |
| 189 | + return *this; |
| 190 | + } |
| 191 | + |
| 192 | + template <class _U = _Tp> |
| 193 | + requires(!is_same_v<remove_cvref_t<_U>, indirect> && is_constructible_v<_Tp, _U> && is_assignable_v<_Tp&, _U>) |
| 194 | + _LIBCPP_HIDE_FROM_ABI constexpr indirect& operator=(_U&& __u) { |
| 195 | + if (valueless_after_move()) |
| 196 | + __p_ = __allocate_owned_object(__alloc_, std::forward<_U>(__u)); |
| 197 | + else |
| 198 | + *__p_ = std::forward<_U>(__u); |
| 199 | + return *this; |
| 200 | + } |
| 201 | + |
| 202 | + // [indirect.obs], observers |
| 203 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept { |
| 204 | + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
| 205 | + !valueless_after_move(), "operator* called on a valueless std::indirect object"); |
| 206 | + return *__p_; |
| 207 | + } |
| 208 | + |
| 209 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept { |
| 210 | + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
| 211 | + !valueless_after_move(), "operator* called on a valueless std::indirect object"); |
| 212 | + return *__p_; |
| 213 | + } |
| 214 | + |
| 215 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept { |
| 216 | + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
| 217 | + !valueless_after_move(), "operator* called on a valueless std::indirect object"); |
| 218 | + return std::move(*__p_); |
| 219 | + } |
| 220 | + |
| 221 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept { |
| 222 | + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
| 223 | + !valueless_after_move(), "operator* called on a valueless std::indirect object"); |
| 224 | + return std::move(*__p_); |
| 225 | + } |
| 226 | + |
| 227 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_pointer operator->() const noexcept { |
| 228 | + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
| 229 | + !valueless_after_move(), "operator-> called on a valueless std::indirect object"); |
| 230 | + return __p_; |
| 231 | + } |
| 232 | + |
| 233 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr pointer operator->() noexcept { |
| 234 | + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( |
| 235 | + !valueless_after_move(), "operator-> called on a valueless std::indirect object"); |
| 236 | + return __p_; |
| 237 | + } |
| 238 | + |
| 239 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool valueless_after_move() const noexcept { return !__p_; } |
| 240 | + |
| 241 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr allocator_type get_allocator() const noexcept { return __alloc_; } |
| 242 | + |
| 243 | + // [indirect.swap], swap |
| 244 | + _LIBCPP_HIDE_FROM_ABI constexpr void |
| 245 | + swap(indirect& __other) noexcept(allocator_traits<_Allocator>::propagate_on_container_swap::value || |
| 246 | + allocator_traits<_Allocator>::is_always_equal::value) { |
| 247 | + _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR( |
| 248 | + allocator_traits<_Allocator>::propagate_on_container_swap::value || get_allocator() == __other.get_allocator(), |
| 249 | + "swapping std::indirect objects with different allocators"); |
| 250 | + std::swap(__p_, __other.__p_); |
| 251 | + std::__swap_allocator(__alloc_, __other.__alloc_); |
| 252 | + } |
| 253 | + |
| 254 | + _LIBCPP_HIDE_FROM_ABI friend constexpr void |
| 255 | + swap(indirect& __lhs, indirect& __rhs) noexcept(noexcept(__lhs.swap(__rhs))) { |
| 256 | + __lhs.swap(__rhs); |
| 257 | + } |
| 258 | + |
| 259 | + // [indirect.relops], relational operators |
| 260 | + template <class _U, class _AA> |
| 261 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool |
| 262 | + operator==(const indirect& __lhs, const indirect<_U, _AA>& __rhs) noexcept(noexcept(*__lhs == *__rhs)) { |
| 263 | + return (__lhs.valueless_after_move() == __rhs.valueless_after_move()) && |
| 264 | + (__lhs.valueless_after_move() || *__lhs == *__rhs); |
| 265 | + } |
| 266 | + |
| 267 | + template <class _U, class _AA> |
| 268 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __synth_three_way_result<_Tp, _U> |
| 269 | + operator<=>(const indirect& __lhs, const indirect<_U, _AA>& __rhs) { |
| 270 | + if (__lhs.valueless_after_move() || __rhs.valueless_after_move()) |
| 271 | + return !__lhs.valueless_after_move() <=> !__rhs.valueless_after_move(); |
| 272 | + return std::__synth_three_way(*__lhs, *__rhs); |
| 273 | + } |
| 274 | + |
| 275 | + // [indirect.comp.with.t], comparison with T |
| 276 | + template <class _U> |
| 277 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool |
| 278 | + operator==(const indirect& __lhs, const _U& __rhs) noexcept(noexcept(*__lhs == __rhs)) { |
| 279 | + return !__lhs.valueless_after_move() && *__lhs == __rhs; |
| 280 | + } |
| 281 | + |
| 282 | + template <class _U> |
| 283 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __synth_three_way_result<_Tp, _U> |
| 284 | + operator<=>(const indirect& __lhs, const _U& __rhs) { |
| 285 | + return __lhs.valueless_after_move() ? strong_ordering::less : std::__synth_three_way(*__lhs, __rhs); |
| 286 | + } |
| 287 | + |
| 288 | +private: |
| 289 | + template <class... _Us> |
| 290 | + _LIBCPP_HIDE_FROM_ABI static constexpr pointer __allocate_owned_object(_Allocator& __a, _Us&&... __us) { |
| 291 | + __allocation_guard<_Allocator> __guard(__a, 1); |
| 292 | + allocator_traits<_Allocator>::construct(__a, __guard.__get(), std::forward<_Us>(__us)...); |
| 293 | + return __guard.__release_ptr(); |
| 294 | + } |
| 295 | + |
| 296 | + _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_owned_object() noexcept { |
| 297 | + if (!valueless_after_move()) { |
| 298 | + allocator_traits<_Allocator>::destroy(__alloc_, __p_); |
| 299 | + allocator_traits<_Allocator>::deallocate(__alloc_, __p_, 1); |
| 300 | + } |
| 301 | + } |
| 302 | + |
| 303 | + _LIBCPP_NO_UNIQUE_ADDRESS _Allocator __alloc_ = _Allocator(); |
| 304 | + pointer __p_; |
| 305 | +}; |
| 306 | + |
| 307 | +template <class _Value> |
| 308 | +indirect(_Value) -> indirect<_Value>; |
| 309 | + |
| 310 | +template <class _Allocator, class _Value> |
| 311 | +indirect(allocator_arg_t, _Allocator, _Value) -> indirect<_Value, __rebind_alloc<allocator_traits<_Allocator>, _Value>>; |
| 312 | + |
| 313 | +template <class _T, class _Allocator> |
| 314 | + requires is_default_constructible_v<hash<_T>> |
| 315 | +struct hash<indirect<_T, _Allocator>> { |
| 316 | + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(const indirect<_T, _Allocator>& __i) const { |
| 317 | + return __i.valueless_after_move() ? 0 : hash<_T>()(*__i); |
| 318 | + } |
| 319 | +}; |
| 320 | + |
| 321 | +namespace pmr { |
| 322 | + |
| 323 | +template <class _Tp> |
| 324 | +using indirect _LIBCPP_AVAILABILITY_PMR = indirect<_Tp, polymorphic_allocator<_Tp>>; |
| 325 | + |
| 326 | +} // namespace pmr |
| 327 | + |
| 328 | +_LIBCPP_END_NAMESPACE_STD |
| 329 | + |
| 330 | +#endif // _LIBCPP_STD_VER >= 26 |
| 331 | + |
| 332 | +#endif // _LIBCPP___MEMORY_INDIRECT_H |
0 commit comments