@@ -130,6 +130,7 @@ bits_freestanding = \
${bits_srcdir}/max_size_type.h \
${bits_srcdir}/memoryfwd.h \
${bits_srcdir}/move.h \
+ ${bits_srcdir}/out_ptr.h \
${bits_srcdir}/predefined_ops.h \
${bits_srcdir}/parse_numbers.h \
${bits_srcdir}/ptr_traits.h \
@@ -485,6 +485,7 @@ bits_freestanding = \
${bits_srcdir}/max_size_type.h \
${bits_srcdir}/memoryfwd.h \
${bits_srcdir}/move.h \
+ ${bits_srcdir}/out_ptr.h \
${bits_srcdir}/predefined_ops.h \
${bits_srcdir}/parse_numbers.h \
${bits_srcdir}/ptr_traits.h \
new file mode 100644
@@ -0,0 +1,432 @@
+// Smart pointer adaptors -*- C++ -*-
+
+// Copyright The GNU Toolchain Authors.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/bits/out_ptr.h
+ * This is an internal header file, included by other library headers.
+ * Do not attempt to use it directly. @headername{memory}
+ */
+
+#ifndef _GLIBCXX_OUT_PTR_H
+#define _GLIBCXX_OUT_PTR_H 1
+
+#pragma GCC system_header
+
+#include <bits/version.h>
+
+#ifdef __glibcxx_out_ptr // C++ >= 23
+
+#include <tuple>
+#include <bits/ptr_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#ifdef __glibcxx_out_ptr // C++ >= 23
+ /// Adapt a smart pointer for functions taking an output pointer parameter.
+ template<typename _Smart, typename _Pointer, typename... _Args>
+ class out_ptr_t
+ {
+ static_assert(!__is_shared_ptr<_Smart> || sizeof...(_Args) != 0,
+ "a deleter must be used when adapting std::shared_ptr "
+ "with std::out_ptr");
+
+ public:
+ explicit
+ out_ptr_t(_Smart& __smart, _Args... __args)
+ : _M_impl{__smart, std::forward<_Args>(__args)...}
+ {
+ if constexpr (requires { _M_impl._M_out_init(); })
+ _M_impl._M_out_init();
+ }
+
+ out_ptr_t(const out_ptr_t&) = delete;
+
+ ~out_ptr_t() = default;
+
+ operator _Pointer*() const noexcept
+ { return _M_impl._M_get(); }
+
+ operator void**() const noexcept requires (!same_as<_Pointer, void*>)
+ {
+ static_assert(is_pointer_v<_Pointer>);
+ _Pointer* __p = *this;
+ return static_cast<void**>(static_cast<void*>(__p));
+ }
+
+ private:
+ // TODO: Move this to namespace scope? e.g. __detail::_Ptr_adapt_impl
+ template<typename, typename, typename...>
+ struct _Impl
+ {
+ // This constructor must not modify __s because out_ptr_t and
+ // inout_ptr_t want to do different things. After construction
+ // they call _M_out_init() or _M_inout_init() respectively.
+ _Impl(_Smart& __s, _Args&&... __args)
+ : _M_smart(__s), _M_args(std::forward<_Args>(__args)...)
+ { }
+
+ // Called by out_ptr_t to clear the smart pointer before using it.
+ void
+ _M_out_init()
+ {
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 3734. Inconsistency in inout_ptr and out_ptr for empty case
+ if constexpr (requires { _M_smart.reset(); })
+ _M_smart.reset();
+ else
+ _M_smart = _Smart();
+ }
+
+ // Called by inout_ptr_t to copy the smart pointer's value
+ // to the pointer that is returned from _M_get().
+ void
+ _M_inout_init()
+ { _M_ptr = _M_smart.release(); }
+
+ // The pointer value returned by operator Pointer*().
+ _Pointer*
+ _M_get() const
+ { return __builtin_addressof(const_cast<_Pointer&>(_M_ptr)); }
+
+ // Finalize the effects on the smart pointer.
+ ~_Impl() noexcept(false);
+
+ _Smart& _M_smart;
+ [[no_unique_address]] _Pointer _M_ptr{};
+ [[no_unique_address]] tuple<_Args...> _M_args;
+ };
+
+ // Partial specialization for raw pointers.
+ template<typename _Tp>
+ struct _Impl<_Tp*, _Tp*>
+ {
+ void
+ _M_out_init()
+ { _M_p = nullptr; }
+
+ void
+ _M_inout_init()
+ { }
+
+ _Tp**
+ _M_get() const
+ { return __builtin_addressof(const_cast<_Tp*&>(_M_p)); }
+
+ _Tp*& _M_p;
+ };
+
+ // Partial specialization for raw pointers, with conversion.
+ template<typename _Tp, typename _Ptr> requires (!is_same_v<_Ptr, _Tp*>)
+ struct _Impl<_Tp*, _Ptr>
+ {
+ explicit
+ _Impl(_Tp*& __p)
+ : _M_p(__p)
+ { }
+
+ void
+ _M_out_init()
+ { _M_p = nullptr; }
+
+ void
+ _M_inout_init()
+ { _M_ptr = _M_p; }
+
+ _Pointer*
+ _M_get() const
+ { return __builtin_addressof(const_cast<_Pointer&>(_M_ptr)); }
+
+ ~_Impl() { _M_p = static_cast<_Tp*>(_M_ptr); }
+
+ _Tp*& _M_p;
+ _Pointer _M_ptr{};
+ };
+
+ // Partial specialization for std::unique_ptr.
+ // This specialization gives direct access to the private member
+ // of the unique_ptr, avoiding the overhead of storing a separate
+ // pointer and then resetting the unique_ptr in the destructor.
+ // FIXME: constrain to only match the primary template,
+ // not program-defined specializations of unique_ptr.
+ template<typename _Tp, typename _Del>
+ struct _Impl<unique_ptr<_Tp, _Del>,
+ typename unique_ptr<_Tp, _Del>::pointer>
+ {
+ void
+ _M_out_init()
+ { _M_smart.reset(); }
+
+ _Pointer*
+ _M_get() const noexcept
+ { return __builtin_addressof(_M_smart._M_t._M_ptr()); }
+
+ _Smart& _M_smart;
+ };
+
+ // Partial specialization for std::unique_ptr with replacement deleter.
+ // FIXME: constrain to only match the primary template,
+ // not program-defined specializations of unique_ptr.
+ template<typename _Tp, typename _Del, typename _Del2>
+ struct _Impl<unique_ptr<_Tp, _Del>,
+ typename unique_ptr<_Tp, _Del>::pointer, _Del2>
+ {
+ void
+ _M_out_init()
+ { _M_smart.reset(); }
+
+ _Pointer*
+ _M_get() const noexcept
+ { return __builtin_addressof(_M_smart._M_t._M_ptr()); }
+
+ ~_Impl()
+ {
+ if (_M_smart.get())
+ _M_smart._M_t._M_deleter() = std::forward<_Del2>(_M_del);
+ }
+
+ _Smart& _M_smart;
+ [[no_unique_address]] _Del2 _M_del;
+ };
+
+ // Partial specialization for std::shared_ptr.
+ // This specialization gives direct access to the private member
+ // of the shared_ptr, avoiding the overhead of storing a separate
+ // pointer and then resetting the shared_ptr in the destructor.
+ // A new control block is allocated in the constructor, so that if
+ // allocation fails it doesn't throw an exception from the destructor.
+ template<typename _Tp, typename _Del, typename _Alloc>
+ requires (is_base_of_v<__shared_ptr<_Tp>, shared_ptr<_Tp>>)
+ struct _Impl<shared_ptr<_Tp>,
+ typename shared_ptr<_Tp>::element_type*, _Del, _Alloc>
+ {
+ _Impl(_Smart& __s, _Del __d, _Alloc __a = _Alloc())
+ : _M_smart(__s)
+ {
+ // We know shared_ptr cannot be used with inout_ptr_t
+ // so we can do all set up here, instead of in _M_out_init().
+ _M_smart.reset();
+
+ // Similar to the shared_ptr(Y*, D, A) constructor, except that if
+ // the allocation throws we do not need (or want) to call deleter.
+ typename _Scd::__allocator_type __a2(__a);
+ auto __mem = __a2.allocate(1);
+ ::new (__mem) _Scd(nullptr, std::forward<_Del>(__d),
+ std::forward<_Alloc>(__a));
+ _M_smart._M_refcount._M_pi = __mem;
+ }
+
+ _Pointer*
+ _M_get() const noexcept
+ { return __builtin_addressof(_M_smart._M_ptr); }
+
+ ~_Impl()
+ {
+ auto& __pi = _M_smart._M_refcount._M_pi;
+
+ if (_Sp __ptr = _M_smart.get())
+ static_cast<_Scd*>(__pi)->_M_impl._M_ptr = __ptr;
+ else // Destroy the control block manually without invoking deleter.
+ std::__exchange(__pi, nullptr)->_M_destroy();
+ }
+
+ _Smart& _M_smart;
+
+ using _Sp = typename _Smart::element_type*;
+ using _Scd = _Sp_counted_deleter<_Sp, decay_t<_Del>,
+ remove_cvref_t<_Alloc>,
+ __default_lock_policy>;
+ };
+
+ // Partial specialization for std::shared_ptr, without custom allocator.
+ template<typename _Tp, typename _Del>
+ requires (is_base_of_v<__shared_ptr<_Tp>, shared_ptr<_Tp>>)
+ struct _Impl<shared_ptr<_Tp>,
+ typename shared_ptr<_Tp>::element_type*, _Del>
+ : _Impl<_Smart, _Pointer, _Del, allocator<void>>
+ {
+ using _Impl<_Smart, _Pointer, _Del, allocator<void>>::_Impl;
+ };
+
+ using _Impl_t = _Impl<_Smart, _Pointer, _Args...>;
+
+ _Impl_t _M_impl;
+
+ template<typename, typename, typename...> friend class inout_ptr_t;
+ };
+
+ /// Adapt a smart pointer for functions taking an output pointer parameter.
+ template<typename _Smart, typename _Pointer, typename... _Args>
+ class inout_ptr_t
+ {
+ static_assert(!__is_shared_ptr<_Smart>,
+ "std::inout_ptr can not be used to wrap std::shared_ptr");
+
+ public:
+ explicit
+ inout_ptr_t(_Smart& __smart, _Args... __args)
+ : _M_impl{__smart, std::forward<_Args>(__args)...}
+ {
+ if constexpr (requires { _M_impl._M_inout_init(); })
+ _M_impl._M_inout_init();
+ }
+
+ inout_ptr_t(const inout_ptr_t&) = delete;
+
+ ~inout_ptr_t() = default;
+
+ operator _Pointer*() const noexcept
+ { return _M_impl._M_get(); }
+
+ operator void**() const noexcept requires (!same_as<_Pointer, void*>)
+ {
+ static_assert(is_pointer_v<_Pointer>);
+ _Pointer* __p = *this;
+ return static_cast<void**>(static_cast<void*>(__p));
+ }
+
+ private:
+ // Avoid an invalid instantiation of out_ptr_t<shared_ptr<T>, ...>
+ using _Out_ptr_t
+ = __conditional_t<__is_shared_ptr<_Smart>,
+ out_ptr_t<void*, void*>,
+ out_ptr_t<_Smart, _Pointer, _Args...>>;
+ using _Impl_t = typename _Out_ptr_t::_Impl_t;
+ _Impl_t _M_impl;
+ };
+
+/// @cond undocumented
+namespace __detail
+{
+ // POINTER_OF metafunction
+ template<typename _Tp>
+ consteval auto
+ __pointer_of()
+ {
+ if constexpr (requires { typename _Tp::pointer; })
+ return type_identity<typename _Tp::pointer>{};
+ else if constexpr (requires { typename _Tp::element_type; })
+ return type_identity<typename _Tp::element_type*>{};
+ else
+ {
+ using _Traits = pointer_traits<_Tp>;
+ if constexpr (requires { typename _Traits::element_type; })
+ return type_identity<typename _Traits::element_type*>{};
+ }
+ // else POINTER_OF(S) is not a valid type, return void.
+ }
+
+ // POINTER_OF_OR metafunction
+ template<typename _Smart, typename _Ptr>
+ consteval auto
+ __pointer_of_or()
+ {
+ using _TypeId = decltype(__detail::__pointer_of<_Smart>());
+ if constexpr (is_void_v<_TypeId>)
+ return type_identity<_Ptr>{};
+ else
+ return _TypeId{};
+ }
+
+ // Returns Pointer if !is_void_v<Pointer>, otherwise POINTER_OF(Smart).
+ template<typename _Ptr, typename _Smart>
+ consteval auto
+ __choose_ptr()
+ {
+ if constexpr (!is_void_v<_Ptr>)
+ return type_identity<_Ptr>{};
+ else
+ return __detail::__pointer_of<_Smart>();
+ }
+
+ template<typename _Smart, typename _Sp, typename... _Args>
+ concept __resettable = requires (_Smart& __s) {
+ __s.reset(std::declval<_Sp>(), std::declval<_Args>()...);
+ };
+}
+/// @endcond
+
+ template<typename _Pointer = void, typename _Smart, typename... _Args>
+ inline auto
+ out_ptr(_Smart& __s, _Args&&... __args)
+ {
+ using _TypeId = decltype(__detail::__choose_ptr<_Pointer, _Smart>());
+ static_assert(!is_void_v<_TypeId>, "first argument to std::out_ptr "
+ "must be a pointer-like type");
+
+ using _Ret = out_ptr_t<_Smart, typename _TypeId::type, _Args&&...>;
+ return _Ret(__s, std::forward<_Args>(__args)...);
+ }
+
+ template<typename _Pointer = void, typename _Smart, typename... _Args>
+ inline auto
+ inout_ptr(_Smart& __s, _Args&&... __args)
+ {
+ using _TypeId = decltype(__detail::__choose_ptr<_Pointer, _Smart>());
+ static_assert(!is_void_v<_TypeId>, "first argument to std::inout_ptr "
+ "must be a pointer-like type");
+
+ using _Ret = inout_ptr_t<_Smart, typename _TypeId::type, _Args&&...>;
+ return _Ret(__s, std::forward<_Args>(__args)...);
+ }
+
+ template<typename _Smart, typename _Pointer, typename... _Args>
+ template<typename _Smart2, typename _Pointer2, typename... _Args2>
+ inline
+ out_ptr_t<_Smart, _Pointer, _Args...>::
+ _Impl<_Smart2, _Pointer2, _Args2...>::~_Impl()
+ {
+ using _TypeId = decltype(__detail::__pointer_of_or<_Smart, _Pointer>());
+ using _Sp = typename _TypeId::type;
+
+ if (!_M_ptr)
+ return;
+
+ _Smart& __s = _M_smart;
+ _Pointer& __p = _M_ptr;
+
+ auto __reset = [&](auto&&... __args) {
+ if constexpr (__detail::__resettable<_Smart, _Sp, _Args...>)
+ __s.reset(static_cast<_Sp>(__p), std::forward<_Args>(__args)...);
+ else if constexpr (is_constructible_v<_Smart, _Sp, _Args...>)
+ __s = _Smart(static_cast<_Sp>(__p), std::forward<_Args>(__args)...);
+ else
+ static_assert(is_constructible_v<_Smart, _Sp, _Args...>);
+ };
+
+ if constexpr (sizeof...(_Args) >= 2)
+ std::apply(__reset, std::move(_M_args));
+ else if constexpr (sizeof...(_Args) == 1)
+ __reset(std::get<0>(std::move(_M_args)));
+ else
+ __reset();
+ }
+#endif // __glibcxx_out_ptr
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+
+#endif // __glibcxx_out_ptr
+#endif /* _GLIBCXX_OUT_PTR_H */
+
@@ -1158,6 +1158,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
};
+#if __cpp_variable_templates
+ template<typename _Tp>
+ static constexpr bool __is_shared_ptr = false;
+ template<typename _Tp>
+ static constexpr bool __is_shared_ptr<shared_ptr<_Tp>> = true;
+#endif
+
/// @} relates shared_ptr
/// @} group pointer_abstractions
@@ -388,11 +388,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @{
*/
- template<typename _Up>
- static constexpr bool __is_shared_ptr = false;
- template<typename _Up>
- static constexpr bool __is_shared_ptr<shared_ptr<_Up>> = true;
-
template<typename _Tp>
class _Sp_atomic
{
@@ -549,6 +549,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
private:
+#ifdef __glibcxx_out_ptr
+ template<typename, typename, typename...> friend class out_ptr_t;
+#endif
_Impl _M_impl;
};
@@ -1126,6 +1129,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#ifdef __glibcxx_atomic_shared_ptr
template<typename> friend class _Sp_atomic;
#endif
+#ifdef __glibcxx_out_ptr
+ template<typename, typename, typename...> friend class out_ptr_t;
+#endif
_Sp_counted_base<_Lp>* _M_pi;
};
@@ -1776,6 +1782,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#ifdef __glibcxx_atomic_shared_ptr
friend _Sp_atomic<shared_ptr<_Tp>>;
#endif
+#ifdef __glibcxx_out_ptr
+ template<typename, typename, typename...> friend class out_ptr_t;
+#endif
element_type* _M_ptr; // Contained pointer.
__shared_count<_Lp> _M_refcount; // Reference counter.
@@ -515,6 +515,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Disable copy from lvalue.
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
+
+ private:
+#ifdef __glibcxx_out_ptr
+ template<typename, typename, typename...>
+ friend class out_ptr_t;
+ template<typename, typename, typename...>
+ friend class inout_ptr_t;
+#endif
};
// 20.7.1.3 unique_ptr for array objects with a runtime length
@@ -789,6 +797,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Disable copy from lvalue.
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
+
+ private:
+#ifdef __glibcxx_out_ptr
+ template<typename, typename, typename...> friend class out_ptr_t;
+ template<typename, typename, typename...> friend class inout_ptr_t;
+#endif
};
/// @{
@@ -1140,6 +1154,13 @@ namespace __detail
}
#endif // C++20 && HOSTED
+#if __cpp_variable_templates
+ template<typename _Tp>
+ static constexpr bool __is_unique_ptr = false;
+ template<typename _Tp, typename _Del>
+ static constexpr bool __is_unique_ptr<unique_ptr<_Tp, _Del>> = true;
+#endif
+
/// @} group pointer_abstractions
#if __cplusplus >= 201703L
@@ -1568,6 +1568,14 @@ ftms = {
};
};
+ftms = {
+ name = out_ptr;
+ values = {
+ v = 202106;
+ cxxmin = 23;
+ };
+};
+
ftms = {
name = spanstream;
values = {
@@ -1923,6 +1923,17 @@
#undef __glibcxx_want_move_only_function
// from version.def line 1572
+#if !defined(__cpp_lib_out_ptr)
+# if (__cplusplus >= 202100L)
+# define __glibcxx_out_ptr 202106L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_out_ptr)
+# define __cpp_lib_out_ptr 202106L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_out_ptr) && defined(__glibcxx_want_out_ptr) */
+#undef __glibcxx_want_out_ptr
+
+// from version.def line 1580
#if !defined(__cpp_lib_spanstream)
# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && (__glibcxx_span)
# define __glibcxx_spanstream 202106L
@@ -1933,7 +1944,7 @@
#endif /* !defined(__cpp_lib_spanstream) && defined(__glibcxx_want_spanstream) */
#undef __glibcxx_want_spanstream
-// from version.def line 1582
+// from version.def line 1590
#if !defined(__cpp_lib_stacktrace)
# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && (_GLIBCXX_HAVE_STACKTRACE)
# define __glibcxx_stacktrace 202011L
@@ -1944,7 +1955,7 @@
#endif /* !defined(__cpp_lib_stacktrace) && defined(__glibcxx_want_stacktrace) */
#undef __glibcxx_want_stacktrace
-// from version.def line 1592
+// from version.def line 1600
#if !defined(__cpp_lib_string_contains)
# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
# define __glibcxx_string_contains 202011L
@@ -1955,7 +1966,7 @@
#endif /* !defined(__cpp_lib_string_contains) && defined(__glibcxx_want_string_contains) */
#undef __glibcxx_want_string_contains
-// from version.def line 1601
+// from version.def line 1609
#if !defined(__cpp_lib_string_resize_and_overwrite)
# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
# define __glibcxx_string_resize_and_overwrite 202110L
@@ -1966,7 +1977,7 @@
#endif /* !defined(__cpp_lib_string_resize_and_overwrite) && defined(__glibcxx_want_string_resize_and_overwrite) */
#undef __glibcxx_want_string_resize_and_overwrite
-// from version.def line 1610
+// from version.def line 1618
#if !defined(__cpp_lib_fstream_native_handle)
# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED
# define __glibcxx_fstream_native_handle 202306L
@@ -1977,7 +1988,7 @@
#endif /* !defined(__cpp_lib_fstream_native_handle) && defined(__glibcxx_want_fstream_native_handle) */
#undef __glibcxx_want_fstream_native_handle
-// from version.def line 1619
+// from version.def line 1627
#if !defined(__cpp_lib_ratio)
# if (__cplusplus > 202302L)
# define __glibcxx_ratio 202306L
@@ -1988,7 +1999,7 @@
#endif /* !defined(__cpp_lib_ratio) && defined(__glibcxx_want_ratio) */
#undef __glibcxx_want_ratio
-// from version.def line 1627
+// from version.def line 1635
#if !defined(__cpp_lib_to_string)
# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars)
# define __glibcxx_to_string 202306L
@@ -91,6 +91,10 @@
# include <bits/uses_allocator_args.h>
#endif
+#if __cplusplus > 202002L
+# include <bits/out_ptr.h>
+#endif
+
#define __glibcxx_want_allocator_traits_is_always_equal
#define __glibcxx_want_assume_aligned
#define __glibcxx_want_atomic_shared_ptr
@@ -99,6 +103,7 @@
#define __glibcxx_want_constexpr_memory
#define __glibcxx_want_enable_shared_from_this
#define __glibcxx_want_make_unique
+#define __glibcxx_want_out_ptr
#define __glibcxx_want_parallel_algorithm
#define __glibcxx_want_ranges
#define __glibcxx_want_raw_memory_algorithms
new file mode 100644
@@ -0,0 +1,47 @@
+// { dg-do run { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+// C++23 [inout.ptr.t] Class template inout_ptr_t
+
+struct star_fish* star_fish_alloc();
+int star_fish_populate(struct star_fish** ps, const char* description);
+
+struct star_fish_deleter {
+ void operator() (struct star_fish* c) const noexcept;
+};
+
+using star_fish_ptr = std::unique_ptr<star_fish, star_fish_deleter>;
+
+// Example 1 from [inout.ptr.t]
+int main(int, char**)
+{
+ star_fish_ptr peach(star_fish_alloc());
+ // ...
+ // used, need to re-make
+ int err = star_fish_populate(std::inout_ptr(peach), "caring clown-fish liker");
+ return 0;
+}
+
+#include <cstdint>
+
+star_fish* star_fish_alloc()
+{
+ static std::uintptr_t counter = 1;
+ return reinterpret_cast<star_fish*>(counter++);
+}
+
+void star_fish_deleter::operator()(star_fish* c) const noexcept
+{
+ static std::uintptr_t counter = 1;
+ VERIFY(reinterpret_cast<std::uintptr_t>(c) == counter++);
+}
+
+int star_fish_populate(star_fish** ps, const char*)
+{
+ VERIFY(ps);
+ star_fish_deleter()(*ps);
+ *ps = star_fish_alloc();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,102 @@
+// { dg-do run { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+// C++23 [inout.ptr.t] Class template inout_ptr_t
+
+int counter = 0;
+
+void
+test_unique_ptr()
+{
+ int* ip = new int(1);
+ std::unique_ptr<int> up(ip);
+ {
+ auto iop = std::inout_ptr(up);
+ int** ipp = iop;
+ VERIFY( *ipp == ip );
+ delete *ipp;
+ *ipp = new int(2);
+ }
+ VERIFY( *up == 2 );
+
+ ip = up.get();
+ {
+ std::default_delete<int> d;
+ auto iop = std::inout_ptr(up, d);
+ int** ipp = iop;
+ VERIFY( *ipp == ip );
+ delete *ipp;
+ *ipp = new int(3);
+ }
+ VERIFY( *up == 3 );
+
+ struct D
+ {
+ explicit D(int id) : id(id) { }
+ void operator()(long* p) const { ++counter; delete p; }
+ int id;
+ };
+ counter = 0;
+ std::unique_ptr<long, D> upd(new long(3), D(11));
+ {
+ auto iop = std::inout_ptr(upd);
+ VERIFY( counter == 0 );
+ long** lpp = iop;
+ VERIFY( **lpp == 3 );
+ delete *lpp;
+ *lpp = new long(4);
+ }
+ VERIFY( *upd == 4 );
+ VERIFY( upd.get_deleter().id == 11 );
+ VERIFY( counter == 0 );
+
+ {
+ D d(22);
+ auto iop = std::inout_ptr(upd, d);
+ VERIFY( counter == 0 );
+ long** lpp = iop;
+ VERIFY( **lpp == 4 );
+ delete *lpp;
+ *lpp = nullptr;
+ }
+ VERIFY( upd == nullptr );
+ VERIFY( upd.get_deleter().id == 11 ); // Deleter not replaced if p is null.
+ VERIFY( counter == 0 );
+
+ upd.reset(new long(5));
+ {
+ D d(33);
+ auto iop = std::inout_ptr(upd, d);
+ VERIFY( counter == 0 );
+ long** lpp = iop;
+ VERIFY( **lpp == 5 );
+ delete *lpp;
+ *lpp = new long(6);
+ }
+ VERIFY( *upd == 6 );
+ VERIFY( upd.get_deleter().id == 33 );
+ VERIFY( counter == 0 );
+
+ struct Base { };
+ struct Derived : Base
+ {
+ Derived(int id) : id(id) { }
+ int id;
+ };
+ std::unique_ptr<Derived> upbd(new Derived(1));
+ {
+ auto iop = std::inout_ptr<Base*>(upbd);
+ Base** bpp = iop;
+ VERIFY( static_cast<Derived*>(*bpp)->id == 1 );
+ delete (Derived*)*bpp;
+ *bpp = new Derived(2);
+ }
+ VERIFY( upbd->id == 2 );
+}
+
+int main()
+{
+ test_unique_ptr();
+}
new file mode 100644
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+
+std::shared_ptr<int> sp;
+auto iop = std::inout_ptr(sp); // { dg-error "here" }
+std::shared_ptr<long> sp2;
+std::default_delete<int> d;
+auto iop2 = std::inout_ptr(sp2, d); // { dg-error "here" }
+
+// { dg-error "can not be used" "" { target *-*-* } 0 }
new file mode 100644
@@ -0,0 +1,35 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+std::unique_ptr<int> intptr;
+
+void
+val_inout(void** p)
+{
+ // The smart pointer should have been released by the inout_ptr_t constructor
+ VERIFY( intptr == nullptr );
+ // The initial value of the pointer should be non-null.
+ VERIFY( *(int*)*p == 5678 );
+ // Although the implementation is allowed to access the unique_ptr's
+ // pointer directly, it can't do that here because the unique_ptr stores
+ // an int* and we are reading from and writing to a void*.
+ VERIFY( intptr.get() != *p );
+
+ // Return a heap-allocated int in *p.
+ *p = (void*) new int(999);
+}
+
+void
+test_inout_ptr_void()
+{
+ intptr.reset(new int(5678));
+ val_inout(std::inout_ptr<void*>(intptr));
+ VERIFY( *intptr == 999 );
+}
+
+int main()
+{
+ test_inout_ptr_void();
+}
new file mode 100644
@@ -0,0 +1,116 @@
+// { dg-do run { target c++23 } }
+// { dg-require-fileio "" }
+// { dg-additional-files "thirty_years_among_the_dead_preproc.txt" }
+
+#include <memory>
+#include <cstdio>
+#include <testsuite_hooks.h>
+
+// C++23 [out.ptr.t] Class template out_ptr_t
+
+int fopen_s(std::FILE** f, const char* name, const char* mode);
+
+struct fclose_deleter {
+ void operator()(std::FILE* f) const noexcept {
+ std::fclose(f);
+ }
+};
+
+// Example 1 from [out.ptr.t]
+void
+test_example_1()
+{
+ constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+ std::unique_ptr<std::FILE, fclose_deleter> file_ptr;
+ int err = fopen_s(std::out_ptr<std::FILE*>(file_ptr), file_name, "r+b");
+ if (err != 0)
+ VERIFY(false);
+ // *file_ptr is valid
+ VERIFY(file_ptr != nullptr);
+}
+
+// Same again without explicit template argument list.
+void
+test_example_1_2()
+{
+ constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+ std::unique_ptr<std::FILE, fclose_deleter> file_ptr;
+ int err = fopen_s(std::out_ptr(file_ptr), file_name, "r+b");
+ if (err != 0)
+ VERIFY(false);
+ // *file_ptr is valid
+ VERIFY(file_ptr != nullptr);
+}
+
+// And again with a deleter argument.
+void
+test_example_1_3()
+{
+ constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+ std::unique_ptr<std::FILE, fclose_deleter> file_ptr;
+ int err = fopen_s(std::out_ptr(file_ptr, fclose_deleter()), file_name, "r+b");
+ if (err != 0)
+ VERIFY(false);
+ // *file_ptr is valid
+ VERIFY(file_ptr != nullptr);
+}
+
+// Same again using std::shared_ptr
+void
+test_example_1_sp()
+{
+ constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+ std::shared_ptr<std::FILE> file_ptr;
+ int err = fopen_s(std::out_ptr<std::FILE*>(file_ptr, fclose_deleter()),
+ file_name, "r+b");
+ if (err != 0)
+ VERIFY(false);
+ // *file_ptr is valid
+ VERIFY(file_ptr != nullptr);
+}
+
+// And again without explicit template argument list.
+void
+test_example_1_sp_2()
+{
+ constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+ std::shared_ptr<std::FILE> file_ptr;
+ int err = fopen_s(std::out_ptr(file_ptr, fclose_deleter()), file_name, "r+b");
+ if (err != 0)
+ VERIFY(false);
+ // *file_ptr is valid
+ VERIFY(file_ptr != nullptr);
+}
+
+// And again using a raw pointer.
+void
+test_example_1_raw()
+{
+ constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+ std::FILE* file_ptr;
+ int err = fopen_s(std::out_ptr(file_ptr), file_name, "r+b");
+ if (err != 0)
+ VERIFY(false);
+ // *file_ptr is valid
+ VERIFY(file_ptr != nullptr);
+ std::fclose(file_ptr);
+}
+
+int main()
+{
+ test_example_1();
+ test_example_1_2();
+ test_example_1_3();
+ test_example_1_sp();
+ test_example_1_sp_2();
+ test_example_1_raw();
+}
+
+#include <cerrno>
+
+int fopen_s(std::FILE** f, const char* name, const char* mode)
+{
+ if ((*f = std::fopen(name, mode)))
+ return 0;
+ return errno;
+}
new file mode 100644
@@ -0,0 +1,273 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+int counter = 0;
+
+void
+test_unique_ptr()
+{
+ std::unique_ptr<int> up(new int(1));
+ {
+ auto op = std::out_ptr(up);
+ VERIFY( up == nullptr );
+ int** p = op;
+ VERIFY( *p == nullptr );
+ *p = new int(2);
+
+ const auto& cop = op;
+ VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+ }
+ VERIFY( *up == 2 );
+
+ {
+ std::default_delete<int> d;
+ auto op = std::out_ptr(up, d);
+ VERIFY( up == nullptr );
+ int** p = op;
+ VERIFY( *p == nullptr );
+ *p = new int(3);
+
+ const auto& cop = op;
+ VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+ }
+ VERIFY( *up == 3 );
+
+ struct D
+ {
+ explicit D(int id) : id(id) { }
+ void operator()(long* p) const { ++counter; delete p; }
+ int id;
+ };
+ counter = 0;
+ std::unique_ptr<long, D> upd(new long(1), D(11));
+ {
+ auto op = std::out_ptr(upd);
+ VERIFY( counter == 1 );
+ VERIFY( upd == nullptr );
+ long** p = op;
+ VERIFY( *p == nullptr );
+ *p = new long(4);
+
+ const auto& cop = op;
+ VERIFY( static_cast<long**>(cop) == static_cast<long**>(op) );
+ }
+ VERIFY( *upd == 4 );
+ VERIFY( upd.get_deleter().id == 11 );
+ VERIFY( counter == 1 );
+
+ {
+ D d(33);
+ auto op = std::out_ptr(upd, d);
+ VERIFY( counter == 2 );
+ VERIFY( upd == nullptr );
+ long** p = op;
+ VERIFY( *p == nullptr );
+
+ const auto& cop = op;
+ VERIFY( static_cast<long**>(cop) == static_cast<long**>(op) );
+ }
+ VERIFY( upd == nullptr );
+ VERIFY( upd.get_deleter().id == 11 ); // Deleter not replaced if p is null.
+ VERIFY( counter == 2 );
+
+ {
+ D d(33);
+ auto op = std::out_ptr(upd, d);
+ VERIFY( counter == 2 );
+ VERIFY( upd == nullptr );
+ long** p = op;
+ VERIFY( *p == nullptr );
+ *p = new long(5);
+ }
+ VERIFY( *upd == 5 );
+ VERIFY( upd.get_deleter().id == 33 );
+ VERIFY( counter == 2 );
+
+ struct Base { };
+ struct Derived : Base
+ {
+ Derived(int id) : id(id) { }
+ int id;
+ };
+ std::unique_ptr<Derived> upbd(new Derived(1));
+ {
+ auto op = std::out_ptr<Base*>(upbd);
+ Base** bpp = op;
+ VERIFY( *bpp == nullptr );
+ *bpp = new Derived(2);
+
+ const auto& cop = op;
+ VERIFY( static_cast<Base**>(cop) == static_cast<Base**>(op) );
+ }
+ VERIFY( upbd->id == 2 );
+}
+
+void deleter_function(float* p) { delete p; }
+
+void
+test_shared_ptr()
+{
+ using DD = std::default_delete<int>;
+
+ std::shared_ptr<int> sp(new int(1));
+ {
+ auto op = std::out_ptr(sp, DD{});
+ VERIFY( sp == nullptr );
+ int** p = op;
+ VERIFY( *p == nullptr );
+ *p = new int(2);
+
+ const auto& cop = op;
+ VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+ }
+ VERIFY( *sp == 2 );
+
+ {
+ auto op = std::out_ptr(sp, DD{});
+ VERIFY( sp == nullptr );
+ int** p = op;
+ VERIFY( *p == nullptr );
+ *p = new int(3);
+ }
+ VERIFY( *sp == 3 );
+
+ struct D
+ {
+ explicit D(int id) : id(id) { }
+ void operator()(long* p) const { ++counter; delete p; }
+ int id;
+ };
+ counter = 0;
+ std::shared_ptr<long> spd(new long(1), D(11));
+ {
+ auto op = std::out_ptr(spd, D(22));
+ VERIFY( counter == 1 );
+ VERIFY( spd == nullptr );
+ long** p = op;
+ VERIFY( *p == nullptr );
+ *p = new long(5);
+ }
+ VERIFY( counter == 1 );
+ VERIFY( *spd == 5 );
+ VERIFY( std::get_deleter<D>(spd)->id == 22 );
+
+ struct Base { };
+ struct Derived : Base
+ {
+ Derived(int id) : id(id) { }
+ int id;
+ };
+ std::shared_ptr<Derived> spbd(new Derived(1));
+ {
+ auto op = std::out_ptr<Base*>(spbd, std::default_delete<Derived>());
+ Base** bpp = op;
+ VERIFY( *bpp == nullptr );
+ *bpp = new Derived(2);
+ }
+ VERIFY( spbd->id == 2 );
+
+ std::shared_ptr<float> spf;
+ {
+ auto op = std::out_ptr(spf, deleter_function);
+ float** fpp = op;
+ *fpp = new float(0.5);
+ }
+ VERIFY( std::get_deleter<void(*)(float*)>(spf) != nullptr );
+ VERIFY( *std::get_deleter<void(*)(float*)>(spf) == &deleter_function );
+ VERIFY( *spf == 0.5 );
+}
+
+void
+test_custom_ptr()
+{
+ struct UPtr : std::unique_ptr<int>
+ {
+ using std::unique_ptr<int>::unique_ptr;
+ };
+
+ UPtr up(new int(1));
+ {
+ auto op = std::out_ptr(up);
+ VERIFY( up == nullptr );
+ int** p = op;
+ VERIFY( *p == nullptr );
+ *p = new int(2);
+
+ const auto& cop = op;
+ VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+ }
+ VERIFY( *up == 2 );
+
+ {
+ auto op = std::out_ptr(up, std::default_delete<int>{});
+ VERIFY( up == nullptr );
+ int** p = op;
+ VERIFY( *p == nullptr );
+ *p = new int(3);
+ }
+ VERIFY( *up == 3 );
+
+ struct D
+ {
+ explicit D(int id) : id(id) { }
+ void operator()(long* p) const { ++counter; delete p; }
+ int id;
+ };
+ counter = 0;
+ struct UDPtr : std::unique_ptr<long, D>
+ {
+ using std::unique_ptr<long, D>::unique_ptr;
+ };
+ UDPtr upd(new long(1), D(11));
+ {
+ auto op = std::out_ptr(upd);
+ VERIFY( counter == 1 );
+ VERIFY( upd == nullptr );
+ long** p = op;
+ VERIFY( *p == nullptr );
+ *p = new long(4);
+ }
+ VERIFY( counter == 1 );
+ VERIFY( *upd == 4 );
+ VERIFY( upd.get_deleter().id == 11 );
+
+ {
+ auto op = std::out_ptr(upd, D(22));
+ VERIFY( upd == nullptr );
+ VERIFY( counter == 2 );
+ long** p = op;
+ VERIFY( *p == nullptr );
+ *p = new long(5);
+ }
+ VERIFY( counter == 2 );
+ VERIFY( *upd == 5 );
+ VERIFY( upd.get_deleter().id == 22 );
+}
+
+void
+test_raw_ptr()
+{
+ long l = 5, l2 = 6;
+ long* lp = &l;
+ {
+ auto op = std::out_ptr(lp);
+ VERIFY( lp == nullptr );
+ long** p = op;
+ VERIFY( *p == nullptr );
+ *p = &l2;
+
+ const auto& cop = op;
+ VERIFY( static_cast<long**>(cop) == static_cast<long**>(op) );
+ }
+ VERIFY( *lp == 6 );
+}
+
+int main()
+{
+ test_unique_ptr();
+ test_shared_ptr();
+ test_custom_ptr();
+ test_raw_ptr();
+}
new file mode 100644
@@ -0,0 +1,7 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+
+std::shared_ptr<int> sp;
+auto op = std::out_ptr(sp); // { dg-error "here" }
+// { dg-error "deleter must be used" "" { target *-*-* } 0 }
new file mode 100644
@@ -0,0 +1,60 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+std::unique_ptr<int> intptr;
+
+void
+val_out(void** p)
+{
+ // The smart pointer should be reset by the out_ptr_t constructor.
+ VERIFY( intptr == nullptr );
+ // The initial value of the pointer should be null.
+ VERIFY( *p == nullptr );
+ // Although the implementation is allowed to access the unique_ptr's
+ // pointer directly, it can't do that here because the unique_ptr stores
+ // an int* and we are writing to a void*.
+ VERIFY( intptr.get() != *p );
+
+ // Return a heap-allocated int in *p.
+ *p = (void*) new int(1234);
+}
+
+void
+test_out_ptr_void()
+{
+ intptr.reset(new int);
+ val_out(std::out_ptr<void*>(intptr));
+ VERIFY( *intptr == 1234 );
+}
+
+void
+val_inout(void** p)
+{
+ // The smart pointer should have been released by the inout_ptr_t constructor
+ VERIFY( intptr == nullptr );
+ // The initial value of the pointer should be non-null.
+ VERIFY( *(int*)*p == 5678 );
+ // Although the implementation is allowed to access the unique_ptr's
+ // pointer directly, it can't do that here because the unique_ptr stores
+ // an int* and we are reading from and writing to a void*.
+ VERIFY( intptr.get() != *p );
+
+ // Return a heap-allocated int in *p.
+ *p = (void*) new int(999);
+}
+
+void
+test_inout_ptr_void()
+{
+ intptr.reset(new int(5678));
+ val_inout(std::inout_ptr<void*>(intptr));
+ VERIFY( *intptr == 999 );
+}
+
+int main()
+{
+ test_out_ptr_void();
+ test_inout_ptr_void();
+}