Skip to content

Commit e475bb7

Browse files
H-G-HristovZingam
andauthored
[libc++][memory] P1132R8: out_ptr - a scalable output pointer abstraction (#73618)
Differential Revision: https://reviews.llvm.org/D150525 Implements: - https://wg21.link/P1132R8 - `out_ptr` - a scalable output pointer abstraction - https://eel.is/c++draft/smartptr.adapt - 20.3.4 Smart pointer adaptors - https://wg21.link/LWG3734 - Inconsistency in `inout_ptr` and `out_ptr` for empty case - https://wg21.link/LWG3897- `inout_ptr` will not update raw pointer to 0 --------- Co-authored-by: Hristo Hristov <[email protected]>
1 parent 1df2e0c commit e475bb7

28 files changed

+1223
-62
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ Status
346346
---------------------------------------------------------- -----------------
347347
``__cpp_lib_optional`` ``202110L``
348348
---------------------------------------------------------- -----------------
349-
``__cpp_lib_out_ptr`` *unimplemented*
349+
``__cpp_lib_out_ptr`` ``202106L``
350350
---------------------------------------------------------- -----------------
351351
``__cpp_lib_print`` ``202207L``
352352
---------------------------------------------------------- -----------------
@@ -450,7 +450,7 @@ Status
450450
---------------------------------------------------------- -----------------
451451
``__cpp_lib_optional_range_support`` *unimplemented*
452452
---------------------------------------------------------- -----------------
453-
``__cpp_lib_out_ptr`` *unimplemented*
453+
``__cpp_lib_out_ptr`` ``202311L``
454454
---------------------------------------------------------- -----------------
455455
``__cpp_lib_philox_engine`` *unimplemented*
456456
---------------------------------------------------------- -----------------

libcxx/docs/ReleaseNotes/19.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ What's New in Libc++ 19.0.0?
3838
Implemented Papers
3939
------------------
4040

41+
- P1132R8 - ``out_ptr`` - a scalable output pointer abstraction
4142
- P2637R3 - Member ``visit``
4243
- P2652R2 - Disallow User Specialization of ``allocator_traits``
4344
- P2819R2 - Add ``tuple`` protocol to ``complex``

libcxx/docs/Status/Cxx23Issues.csv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@
192192
"`3515 <https://wg21.link/LWG3515>`__","§[stacktrace.basic.nonmem]: ``operator<<`` should be less templatized", "November 2022","","",""
193193
"`3545 <https://wg21.link/LWG3545>`__","``std::pointer_traits`` should be SFINAE-friendly", "November 2022","|Complete|","18.0",""
194194
"`3569 <https://wg21.link/LWG3569>`__","``join_view`` fails to support ranges of ranges with non-default_initializable iterators", "November 2022","","","|ranges|"
195-
"`3594 <https://wg21.link/LWG3594>`__","``inout_ptr`` — inconsistent ``release()`` in destructor", "November 2022","","",""
195+
"`3594 <https://wg21.link/LWG3594>`__","``inout_ptr`` — inconsistent ``release()`` in destructor", "November 2022","|Complete|","19.0",""
196196
"`3597 <https://wg21.link/LWG3597>`__","Unsigned integer types don't model advanceable", "November 2022","","","|ranges|"
197197
"`3600 <https://wg21.link/LWG3600>`__","Making ``istream_iterator`` copy constructor trivial is an ABI break", "November 2022","","",""
198198
"`3629 <https://wg21.link/LWG3629>`__","``make_error_code`` and ``make_error_condition`` are customization points","November 2022","|Complete|","16.0",""
@@ -282,7 +282,7 @@
282282
"`3645 <https://wg21.link/LWG3645>`__","``resize_and_overwrite`` is overspecified to call its callback with lvalues","February 2023","|Complete|","14.0",""
283283
"`3655 <https://wg21.link/LWG3655>`__","The ``INVOKE`` operation and union types","February 2023","|Complete|","18.0",""
284284
"`3723 <https://wg21.link/LWG3723>`__","``priority_queue::push_range`` needs to ``append_range``","February 2023","","","|ranges|"
285-
"`3734 <https://wg21.link/LWG3734>`__","Inconsistency in ``inout_ptr`` and ``out_ptr`` for empty case","February 2023","","",""
285+
"`3734 <https://wg21.link/LWG3734>`__","Inconsistency in ``inout_ptr`` and ``out_ptr`` for empty case","February 2023","|Complete|","19.0",""
286286
"`3772 <https://wg21.link/LWG3772>`__","``repeat_view``'s ``piecewise`` constructor is missing Postconditions","February 2023","|Complete|","17.0","|ranges|"
287287
"`3786 <https://wg21.link/LWG3786>`__","Flat maps' deduction guide needs to default ``Allocator`` to be useful","February 2023","","",""
288288
"`3803 <https://wg21.link/LWG3803>`__","``flat_foo`` constructors taking ``KeyContainer`` lack ``KeyCompare`` parameter","February 2023","","",""

libcxx/docs/Status/Cxx23Papers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"","","","","","",""
1414
"`P0401R6 <https://wg21.link/P0401R6>`__","LWG","Providing size feedback in the Allocator interface","June 2021","|Complete|","15.0"
1515
"`P0448R4 <https://wg21.link/P0448R4>`__","LWG","A strstream replacement using span<charT> as buffer","June 2021","",""
16-
"`P1132R8 <https://wg21.link/P1132R8>`__","LWG","out_ptr - a scalable output pointer abstraction","June 2021","",""
16+
"`P1132R8 <https://wg21.link/P1132R8>`__","LWG","out_ptr - a scalable output pointer abstraction","June 2021","|Complete|","19.0"
1717
"`P1328R1 <https://wg21.link/P1328R1>`__","LWG","Making std::type_info::operator== constexpr","June 2021","|Complete|","17.0"
1818
"`P1425R4 <https://wg21.link/P1425R4>`__","LWG","Iterators pair constructors for stack and queue","June 2021","|Complete|","14.0","|ranges|"
1919
"`P1518R2 <https://wg21.link/P1518R2>`__","LWG","Stop overconstraining allocators in container deduction guides","June 2021","|Complete|","13.0"

libcxx/docs/Status/Cxx2cIssues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"`3749 <https://wg21.link/LWG3749>`__","``common_iterator`` should handle integer-class difference types","Kona November 2023","","",""
2525
"`3809 <https://wg21.link/LWG3809>`__","Is ``std::subtract_with_carry_engine<uint16_t>`` supposed to work","Kona November 2023","","",""
2626
"`3892 <https://wg21.link/LWG3892>`__","Incorrect formatting of nested ranges and tuples","Kona November 2023","|Complete|","17.0","|format|"
27-
"`3897 <https://wg21.link/LWG3897>`__","``inout_ptr`` will not update raw pointer to 0","Kona November 2023","","",""
27+
"`3897 <https://wg21.link/LWG3897>`__","``inout_ptr`` will not update raw pointer to 0","Kona November 2023","|Complete|","19.0",""
2828
"`3946 <https://wg21.link/LWG3946>`__","The definition of ``const_iterator_t`` should be reworked","Kona November 2023","","",""
2929
"`3947 <https://wg21.link/LWG3947>`__","Unexpected constraints on ``adjacent_transform_view::base()``","Kona November 2023","","","|ranges|"
3030
"`3948 <https://wg21.link/LWG3948>`__","``possibly-const-range and as-const-pointer`` should be ``noexcept``","Kona November 2023","","","|ranges|"

libcxx/include/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,8 @@ set(files
533533
__memory/concepts.h
534534
__memory/construct_at.h
535535
__memory/destruct_n.h
536+
__memory/inout_ptr.h
537+
__memory/out_ptr.h
536538
__memory/pointer_traits.h
537539
__memory/ranges_construct_at.h
538540
__memory/ranges_uninitialized_algorithms.h

libcxx/include/__memory/allocator_traits.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
4141
struct NAME<_Tp, __void_t<typename _Tp::PROPERTY > > : true_type {}
4242

4343
// __pointer
44-
_LIBCPP_ALLOCATOR_TRAITS_HAS_XXX(__has_pointer, pointer);
4544
template <class _Tp,
4645
class _Alloc,
4746
class _RawAlloc = __libcpp_remove_reference_t<_Alloc>,

libcxx/include/__memory/inout_ptr.h

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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___INOUT_PTR_H
11+
#define _LIBCPP___INOUT_PTR_H
12+
13+
#include <__config>
14+
#include <__memory/addressof.h>
15+
#include <__memory/pointer_traits.h>
16+
#include <__memory/shared_ptr.h>
17+
#include <__memory/unique_ptr.h>
18+
#include <__type_traits/is_same.h>
19+
#include <__type_traits/is_specialization.h>
20+
#include <__type_traits/is_void.h>
21+
#include <__utility/forward.h>
22+
#include <__utility/move.h>
23+
#include <tuple>
24+
25+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
26+
# pragma GCC system_header
27+
#endif
28+
29+
_LIBCPP_PUSH_MACROS
30+
#include <__undef_macros>
31+
32+
_LIBCPP_BEGIN_NAMESPACE_STD
33+
34+
#if _LIBCPP_STD_VER >= 23
35+
36+
template <class _Smart, class _Pointer, class... _Args>
37+
class _LIBCPP_TEMPLATE_VIS inout_ptr_t {
38+
static_assert(!__is_specialization_v<_Smart, shared_ptr>, "std::shared_ptr<> is not supported with std::inout_ptr.");
39+
40+
public:
41+
_LIBCPP_HIDE_FROM_ABI explicit inout_ptr_t(_Smart& __smart, _Args... __args)
42+
: __s_(__smart), __a_(std::forward<_Args>(__args)...), __p_([&__smart] {
43+
if constexpr (is_pointer_v<_Smart>) {
44+
return __smart;
45+
} else {
46+
return __smart.get();
47+
}
48+
}()) {
49+
if constexpr (requires { __s_.release(); }) {
50+
__s_.release();
51+
} else {
52+
__s_ = _Smart();
53+
}
54+
}
55+
56+
_LIBCPP_HIDE_FROM_ABI inout_ptr_t(const inout_ptr_t&) = delete;
57+
58+
_LIBCPP_HIDE_FROM_ABI ~inout_ptr_t() {
59+
// LWG-3897 inout_ptr will not update raw pointer to null
60+
if constexpr (!is_pointer_v<_Smart>) {
61+
if (!__p_) {
62+
return;
63+
}
64+
}
65+
66+
using _SP = __pointer_of_or_t<_Smart, _Pointer>;
67+
if constexpr (is_pointer_v<_Smart>) {
68+
std::apply([&](auto&&... __args) { __s_ = _Smart(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
69+
std::move(__a_));
70+
} else if constexpr (__resettable_smart_pointer_with_args<_Smart, _Pointer, _Args...>) {
71+
std::apply([&](auto&&... __args) { __s_.reset(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
72+
std::move(__a_));
73+
} else {
74+
static_assert(is_constructible_v<_Smart, _SP, _Args...>,
75+
"The smart pointer must be constructible from arguments of types _Smart, _Pointer, _Args...");
76+
std::apply([&](auto&&... __args) { __s_ = _Smart(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
77+
std::move(__a_));
78+
}
79+
}
80+
81+
_LIBCPP_HIDE_FROM_ABI operator _Pointer*() const noexcept { return std::addressof(const_cast<_Pointer&>(__p_)); }
82+
83+
_LIBCPP_HIDE_FROM_ABI operator void**() const noexcept
84+
requires(!is_same_v<_Pointer, void*>)
85+
{
86+
static_assert(is_pointer_v<_Pointer>, "The conversion to void** requires _Pointer to be a raw pointer.");
87+
88+
return reinterpret_cast<void**>(static_cast<_Pointer*>(*this));
89+
}
90+
91+
private:
92+
_Smart& __s_;
93+
tuple<_Args...> __a_;
94+
_Pointer __p_;
95+
};
96+
97+
template <class _Pointer = void, class _Smart, class... _Args>
98+
_LIBCPP_HIDE_FROM_ABI auto inout_ptr(_Smart& __s, _Args&&... __args) {
99+
using _Ptr = conditional_t<is_void_v<_Pointer>, __pointer_of_t<_Smart>, _Pointer>;
100+
return std::inout_ptr_t<_Smart, _Ptr, _Args&&...>(__s, std::forward<_Args>(__args)...);
101+
}
102+
103+
#endif // _LIBCPP_STD_VER >= 23
104+
105+
_LIBCPP_END_NAMESPACE_STD
106+
107+
_LIBCPP_POP_MACROS
108+
109+
#endif // _LIBCPP___INOUT_PTR_H

libcxx/include/__memory/out_ptr.h

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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___OUT_PTR_H
11+
#define _LIBCPP___OUT_PTR_H
12+
13+
#include <__config>
14+
#include <__memory/addressof.h>
15+
#include <__memory/pointer_traits.h>
16+
#include <__memory/shared_ptr.h>
17+
#include <__memory/unique_ptr.h>
18+
#include <__type_traits/is_specialization.h>
19+
#include <__type_traits/is_void.h>
20+
#include <__utility/forward.h>
21+
#include <__utility/move.h>
22+
#include <tuple>
23+
24+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
25+
# pragma GCC system_header
26+
#endif
27+
28+
_LIBCPP_PUSH_MACROS
29+
#include <__undef_macros>
30+
31+
_LIBCPP_BEGIN_NAMESPACE_STD
32+
33+
#if _LIBCPP_STD_VER >= 23
34+
35+
template <class _Smart, class _Pointer, class... _Args>
36+
class _LIBCPP_TEMPLATE_VIS out_ptr_t {
37+
static_assert(!__is_specialization_v<_Smart, shared_ptr> || sizeof...(_Args) > 0,
38+
"Using std::shared_ptr<> without a deleter in std::out_ptr is not supported.");
39+
40+
public:
41+
_LIBCPP_HIDE_FROM_ABI explicit out_ptr_t(_Smart& __smart, _Args... __args)
42+
: __s_(__smart), __a_(std::forward<_Args>(__args)...), __p_() {
43+
using _Ptr = decltype(__smart);
44+
if constexpr (__resettable_smart_pointer<_Ptr>) {
45+
__s_.reset();
46+
} else if constexpr (is_constructible_v<_Smart>) {
47+
__s_ = _Smart();
48+
} else {
49+
static_assert(__resettable_smart_pointer<_Ptr> || is_constructible_v<_Smart>,
50+
"The adapted pointer type must have a reset() member function or be default constructible.");
51+
}
52+
}
53+
54+
_LIBCPP_HIDE_FROM_ABI out_ptr_t(const out_ptr_t&) = delete;
55+
56+
_LIBCPP_HIDE_FROM_ABI ~out_ptr_t() {
57+
if (!__p_) {
58+
return;
59+
}
60+
61+
using _SP = __pointer_of_or_t<_Smart, _Pointer>;
62+
if constexpr (__resettable_smart_pointer_with_args<_Smart, _Pointer, _Args...>) {
63+
std::apply([&](auto&&... __args) { __s_.reset(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
64+
std::move(__a_));
65+
} else {
66+
static_assert(is_constructible_v<_Smart, _SP, _Args...>,
67+
"The smart pointer must be constructible from arguments of types _Smart, _Pointer, _Args...");
68+
std::apply([&](auto&&... __args) { __s_ = _Smart(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
69+
std::move(__a_));
70+
}
71+
}
72+
73+
_LIBCPP_HIDE_FROM_ABI operator _Pointer*() const noexcept { return std::addressof(const_cast<_Pointer&>(__p_)); }
74+
75+
_LIBCPP_HIDE_FROM_ABI operator void**() const noexcept
76+
requires(!is_same_v<_Pointer, void*>)
77+
{
78+
static_assert(is_pointer_v<_Pointer>, "The conversion to void** requires _Pointer to be a raw pointer.");
79+
80+
return reinterpret_cast<void**>(static_cast<_Pointer*>(*this));
81+
}
82+
83+
private:
84+
_Smart& __s_;
85+
tuple<_Args...> __a_;
86+
_Pointer __p_ = _Pointer();
87+
};
88+
89+
template <class _Pointer = void, class _Smart, class... _Args>
90+
_LIBCPP_HIDE_FROM_ABI auto out_ptr(_Smart& __s, _Args&&... __args) {
91+
using _Ptr = conditional_t<is_void_v<_Pointer>, __pointer_of_t<_Smart>, _Pointer>;
92+
return std::out_ptr_t<_Smart, _Ptr, _Args&&...>(__s, std::forward<_Args>(__args)...);
93+
}
94+
95+
#endif // _LIBCPP_STD_VER >= 23
96+
97+
_LIBCPP_END_NAMESPACE_STD
98+
99+
_LIBCPP_POP_MACROS
100+
101+
#endif // _LIBCPP___OUT_PTR_H

libcxx/include/__memory/pointer_traits.h

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,28 @@
2020
#include <__type_traits/is_void.h>
2121
#include <__type_traits/void_t.h>
2222
#include <__utility/declval.h>
23+
#include <__utility/forward.h>
2324
#include <cstddef>
2425

2526
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
2627
# pragma GCC system_header
2728
#endif
2829

30+
_LIBCPP_PUSH_MACROS
31+
#include <__undef_macros>
32+
2933
_LIBCPP_BEGIN_NAMESPACE_STD
3034

31-
template <class _Tp, class = void>
32-
struct __has_element_type : false_type {};
35+
// clang-format off
36+
#define _LIBCPP_CLASS_TRAITS_HAS_XXX(NAME, PROPERTY) \
37+
template <class _Tp, class = void> \
38+
struct NAME : false_type {}; \
39+
template <class _Tp> \
40+
struct NAME<_Tp, __void_t<typename _Tp::PROPERTY> > : true_type {}
41+
// clang-format on
3342

34-
template <class _Tp>
35-
struct __has_element_type<_Tp, __void_t<typename _Tp::element_type> > : true_type {};
43+
_LIBCPP_CLASS_TRAITS_HAS_XXX(__has_pointer, pointer);
44+
_LIBCPP_CLASS_TRAITS_HAS_XXX(__has_element_type, element_type);
3645

3746
template <class _Ptr, bool = __has_element_type<_Ptr>::value>
3847
struct __pointer_traits_element_type {};
@@ -240,6 +249,59 @@ to_address(const _Pointer& __p) noexcept -> decltype(std::__to_address(__p)) {
240249
}
241250
#endif
242251

252+
#if _LIBCPP_STD_VER >= 23
253+
254+
template <class _Tp>
255+
struct __pointer_of {};
256+
257+
template <class _Tp>
258+
requires(__has_pointer<_Tp>::value)
259+
struct __pointer_of<_Tp> {
260+
using type = typename _Tp::pointer;
261+
};
262+
263+
template <class _Tp>
264+
requires(!__has_pointer<_Tp>::value && __has_element_type<_Tp>::value)
265+
struct __pointer_of<_Tp> {
266+
using type = typename _Tp::element_type*;
267+
};
268+
269+
template <class _Tp>
270+
requires(!__has_pointer<_Tp>::value && !__has_element_type<_Tp>::value &&
271+
__has_element_type<pointer_traits<_Tp>>::value)
272+
struct __pointer_of<_Tp> {
273+
using type = typename pointer_traits<_Tp>::element_type*;
274+
};
275+
276+
template <typename _Tp>
277+
using __pointer_of_t = typename __pointer_of<_Tp>::type;
278+
279+
template <class _Tp, class _Up>
280+
struct __pointer_of_or {
281+
using type _LIBCPP_NODEBUG = _Up;
282+
};
283+
284+
template <class _Tp, class _Up>
285+
requires requires { typename __pointer_of_t<_Tp>; }
286+
struct __pointer_of_or<_Tp, _Up> {
287+
using type _LIBCPP_NODEBUG = __pointer_of_t<_Tp>;
288+
};
289+
290+
template <typename _Tp, typename _Up>
291+
using __pointer_of_or_t = typename __pointer_of_or<_Tp, _Up>::type;
292+
293+
template <class _Smart>
294+
concept __resettable_smart_pointer = requires(_Smart __s) { __s.reset(); };
295+
296+
template <class _Smart, class _Pointer, class... _Args>
297+
concept __resettable_smart_pointer_with_args = requires(_Smart __s, _Pointer __p, _Args... __args) {
298+
__s.reset(static_cast<__pointer_of_or_t<_Smart, _Pointer>>(__p), std::forward<_Args>(__args)...);
299+
};
300+
301+
#endif
302+
243303
_LIBCPP_END_NAMESPACE_STD
244304

305+
_LIBCPP_POP_MACROS
306+
245307
#endif // _LIBCPP___MEMORY_POINTER_TRAITS_H

0 commit comments

Comments
 (0)