Skip to content

Commit 0cf68e8

Browse files
committed
[libc++] Implement the indirect half of P3019R11: Vocabulary Types for Composite Class Design
1 parent 6986f12 commit 0cf68e8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2520
-7
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ Status
472472
---------------------------------------------------------- -----------------
473473
``__cpp_lib_hazard_pointer`` *unimplemented*
474474
---------------------------------------------------------- -----------------
475+
``__cpp_lib_indirect`` ``202502L``
476+
---------------------------------------------------------- -----------------
475477
``__cpp_lib_inplace_vector`` *unimplemented*
476478
---------------------------------------------------------- -----------------
477479
``__cpp_lib_is_sufficiently_aligned`` ``202411L``

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
"`P2846R6 <https://wg21.link/P2846R6>`__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 <https://github.com/llvm/llvm-project/issues/127884>`__",""
116116
"`P3471R4 <https://wg21.link/P3471R4>`__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 <https://github.com/llvm/llvm-project/issues/127885>`__",""
117117
"`P0447R28 <https://wg21.link/P0447R28>`__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 <https://github.com/llvm/llvm-project/issues/127886>`__",""
118-
"`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__",""
118+
"`P3019R14 <https://wg21.link/P3019R14>`__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","|Partial|","","`#127887 <https://github.com/llvm/llvm-project/issues/127887>`__","``polymorphic`` is not yet implemented."
119119
"","","","","","",""
120120
"`P2996R13 <https://wg21.link/P2996R13>`__","Reflection for C++26","2025-06 (Sofia)","","","`#148123 <https://github.com/llvm/llvm-project/issues/148123>`__",""
121121
"`P3394R4 <https://wg21.link/P3394R4>`__","Annotations for Reflection","2025-06 (Sofia)","","","`#148124 <https://github.com/llvm/llvm-project/issues/148124>`__",""

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,7 @@ set(files
582582
__memory/construct_at.h
583583
__memory/destroy.h
584584
__memory/destruct_n.h
585+
__memory/indirect.h
585586
__memory/inout_ptr.h
586587
__memory/is_sufficiently_aligned.h
587588
__memory/noexcept_move_assign_container.h

libcxx/include/__memory/indirect.h

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
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

Comments
 (0)