From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 0A2863882676 for ; Thu, 16 Nov 2023 08:13:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 0A2863882676 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 0A2863882676 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700122413; cv=none; b=idqrtSFyWBcrHDVjfbMUeVARbNkKVIVrc82RQkQ5YsDvT5+YUPttviUDFYrdjJzyim2RCuvKbrvx/9ilV90RrSyGTKITEfErw2n8ywrfSkmYdttweaO1Juepdv5lUAv4D417FKdhGp0dmfaAGX6i2tZMSieNiicZRB7qu/k7/F4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700122413; c=relaxed/simple; bh=cairLGXj1iB8GJomgculECwFshGRNinhpYA8gJjzTZ0=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=cu6M0oBLvYQ0h6RbwL3wCY1gbuIbbZNDDgdy4b6gVcPyysnquPhIzZzmTrkitAKUaYXIU3vOwFfRVb4QdVaA0C/N4lqp3m0rFBsoFmtFU7v/c8V4kijPUIL3CGxTzUEQDqZxZXgCNwkj1xMfv9asa7Xn8eMkewqvEQoNDTbgXMc= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700122407; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=yiTJoC0hX4iswTHYICMtQUk+1VJiCRlh1c3G4bcopcE=; b=fSiLShTa3/1H7x61+jNP4I/O6lTFpFgI1DdKbgl4Z8OGQnEZuF3S2Vq653y/fpyesqakAj up3SyPadfq1GEZMREFL+LPb91JcdTiZxQqu01rxbDQnS2hu7hnm51/qP6o9Dno/CRJgfWV HVHDq1X0tvBySGfdvz324kxGGQMOwB0= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-97-hFoysFnPNjiLpMD2-RJh6g-1; Thu, 16 Nov 2023 03:13:25 -0500 X-MC-Unique: hFoysFnPNjiLpMD2-RJh6g-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 961B4101A54C; Thu, 16 Nov 2023 08:13:25 +0000 (UTC) Received: from localhost (unknown [10.42.28.9]) by smtp.corp.redhat.com (Postfix) with ESMTP id 16BAA1121306; Thu, 16 Nov 2023 08:13:24 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Implement std::out_ptr and std::inout_ptr for C++23 [PR111667] Date: Thu, 16 Nov 2023 08:12:39 +0000 Message-ID: <20231116081324.1221069-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.3 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Tested x86_64-linux. Pushe to trunk. -- >8 -- This implements that changes from P1132R8, including optimized paths for std::shared_ptr and std::unique_ptr. For std::shared_ptr we pre-allocate a new control block in the std::out_ptr_t constructor so that the destructor is non-throwing. This requires some care because unlike the shared_ptr(Y*, D, A) constructor, we don't want to invoke the deleter if allocating the control block throws, because we don't own any pointer yet. In order to avoid the unwanted deleter invocation, we create the control block manually. We also want to avoid invoking the deleter on a null pointer on destruction, so we destroy the control block manually if there is no pointer to take ownership of. For std::unique_ptr and for raw pointers, the out_ptr_t object hands out direct access to the pointer, so that we don't have anything to do (except possibly assign a new deleter) in the ~out_ptr_t destructor. These optimizations avoid requiring additional temporary storage for the pointer (and optional arguments), and avoid additional instructions to copy that pointer into the smart pointer at the end. libstdc++-v3/ChangeLog: PR libstdc++/111667 * include/Makefile.am: Add new header. * include/Makefile.in: Regenerate. * include/bits/out_ptr.h: New file. * include/bits/shared_ptr.h (__is_shared_ptr): Move definition to here ... * include/bits/shared_ptr_atomic.h (__is_shared_ptr): ... from here. * include/bits/shared_ptr_base.h (__shared_count): Declare out_ptr_t as a friend. (_Sp_counted_deleter, __shared_ptr): Likewise. * include/bits/unique_ptr.h (unique_ptr, unique_ptr): Declare out_ptr_t and inout_ptr_t as friends. (__is_unique_ptr): Define new variable template. * include/bits/version.def (out_ptr): Define. * include/bits/version.h: Regenerate. * include/std/memory: Include new header. * testsuite/20_util/smartptr.adapt/inout_ptr/1.cc: New test. * testsuite/20_util/smartptr.adapt/inout_ptr/2.cc: New test. * testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc: New test. * testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc: New test. * testsuite/20_util/smartptr.adapt/out_ptr/1.cc: New test. * testsuite/20_util/smartptr.adapt/out_ptr/2.cc: New test. * testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc: New test. * testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc: New test. --- libstdc++-v3/include/Makefile.am | 1 + libstdc++-v3/include/Makefile.in | 1 + libstdc++-v3/include/bits/out_ptr.h | 432 ++++++++++++++++++ libstdc++-v3/include/bits/shared_ptr.h | 7 + libstdc++-v3/include/bits/shared_ptr_atomic.h | 5 - libstdc++-v3/include/bits/shared_ptr_base.h | 9 + libstdc++-v3/include/bits/unique_ptr.h | 21 + libstdc++-v3/include/bits/version.def | 8 + libstdc++-v3/include/bits/version.h | 23 +- libstdc++-v3/include/std/memory | 5 + .../20_util/smartptr.adapt/inout_ptr/1.cc | 47 ++ .../20_util/smartptr.adapt/inout_ptr/2.cc | 102 +++++ .../inout_ptr/shared_ptr_neg.cc | 11 + .../smartptr.adapt/inout_ptr/void_ptr.cc | 35 ++ .../20_util/smartptr.adapt/out_ptr/1.cc | 116 +++++ .../20_util/smartptr.adapt/out_ptr/2.cc | 273 +++++++++++ .../smartptr.adapt/out_ptr/shared_ptr_neg.cc | 7 + .../smartptr.adapt/out_ptr/void_ptr.cc | 60 +++ 18 files changed, 1152 insertions(+), 11 deletions(-) create mode 100644 libstdc++-v3/include/bits/out_ptr.h create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/1.cc create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/2.cc create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/1.cc create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/2.cc create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc create mode 100644 libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 6209f390e08..dab9f720cbb 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -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 \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 596fa0d2390..4f7ab2dfbab 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -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 \ diff --git a/libstdc++-v3/include/bits/out_ptr.h b/libstdc++-v3/include/bits/out_ptr.h new file mode 100644 index 00000000000..49712fa7e31 --- /dev/null +++ b/libstdc++-v3/include/bits/out_ptr.h @@ -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 +// . + +/** @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 + +#ifdef __glibcxx_out_ptr // C++ >= 23 + +#include +#include + +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 + 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(static_cast(__p)); + } + + private: + // TODO: Move this to namespace scope? e.g. __detail::_Ptr_adapt_impl + template + 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 + 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 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 + struct _Impl, + 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 + struct _Impl, + 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 + requires (is_base_of_v<__shared_ptr<_Tp>, shared_ptr<_Tp>>) + struct _Impl, + 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 + requires (is_base_of_v<__shared_ptr<_Tp>, shared_ptr<_Tp>>) + struct _Impl, + typename shared_ptr<_Tp>::element_type*, _Del> + : _Impl<_Smart, _Pointer, _Del, allocator> + { + using _Impl<_Smart, _Pointer, _Del, allocator>::_Impl; + }; + + using _Impl_t = _Impl<_Smart, _Pointer, _Args...>; + + _Impl_t _M_impl; + + template friend class inout_ptr_t; + }; + + /// Adapt a smart pointer for functions taking an output pointer parameter. + template + 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(static_cast(__p)); + } + + private: + // Avoid an invalid instantiation of out_ptr_t, ...> + using _Out_ptr_t + = __conditional_t<__is_shared_ptr<_Smart>, + out_ptr_t, + 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 + consteval auto + __pointer_of() + { + if constexpr (requires { typename _Tp::pointer; }) + return type_identity{}; + else if constexpr (requires { typename _Tp::element_type; }) + return type_identity{}; + else + { + using _Traits = pointer_traits<_Tp>; + if constexpr (requires { typename _Traits::element_type; }) + return type_identity{}; + } + // else POINTER_OF(S) is not a valid type, return void. + } + + // POINTER_OF_OR metafunction + template + 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, otherwise POINTER_OF(Smart). + template + consteval auto + __choose_ptr() + { + if constexpr (!is_void_v<_Ptr>) + return type_identity<_Ptr>{}; + else + return __detail::__pointer_of<_Smart>(); + } + + template + concept __resettable = requires (_Smart& __s) { + __s.reset(std::declval<_Sp>(), std::declval<_Args>()...); + }; +} +/// @endcond + + template + 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 + 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 + template + 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 */ + diff --git a/libstdc++-v3/include/bits/shared_ptr.h b/libstdc++-v3/include/bits/shared_ptr.h index 02bfdabfd1c..e3c48dead72 100644 --- a/libstdc++-v3/include/bits/shared_ptr.h +++ b/libstdc++-v3/include/bits/shared_ptr.h @@ -1158,6 +1158,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } }; +#if __cpp_variable_templates + template + static constexpr bool __is_shared_ptr = false; + template + static constexpr bool __is_shared_ptr> = true; +#endif + /// @} relates shared_ptr /// @} group pointer_abstractions diff --git a/libstdc++-v3/include/bits/shared_ptr_atomic.h b/libstdc++-v3/include/bits/shared_ptr_atomic.h index d0be43aec2d..22b1a1d6c50 100644 --- a/libstdc++-v3/include/bits/shared_ptr_atomic.h +++ b/libstdc++-v3/include/bits/shared_ptr_atomic.h @@ -388,11 +388,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @{ */ - template - static constexpr bool __is_shared_ptr = false; - template - static constexpr bool __is_shared_ptr> = true; - template class _Sp_atomic { diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h index edb3eb654ea..1961a9ae9e9 100644 --- a/libstdc++-v3/include/bits/shared_ptr_base.h +++ b/libstdc++-v3/include/bits/shared_ptr_base.h @@ -549,6 +549,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } private: +#ifdef __glibcxx_out_ptr + template friend class out_ptr_t; +#endif _Impl _M_impl; }; @@ -1126,6 +1129,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #ifdef __glibcxx_atomic_shared_ptr template friend class _Sp_atomic; #endif +#ifdef __glibcxx_out_ptr + template 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>; #endif +#ifdef __glibcxx_out_ptr + template friend class out_ptr_t; +#endif element_type* _M_ptr; // Contained pointer. __shared_count<_Lp> _M_refcount; // Reference counter. diff --git a/libstdc++-v3/include/bits/unique_ptr.h b/libstdc++-v3/include/bits/unique_ptr.h index 164f5f7e945..9d6f5af0a04 100644 --- a/libstdc++-v3/include/bits/unique_ptr.h +++ b/libstdc++-v3/include/bits/unique_ptr.h @@ -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 + friend class out_ptr_t; + template + 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 friend class out_ptr_t; + template friend class inout_ptr_t; +#endif }; /// @{ @@ -1140,6 +1154,13 @@ namespace __detail } #endif // C++20 && HOSTED +#if __cpp_variable_templates + template + static constexpr bool __is_unique_ptr = false; + template + static constexpr bool __is_unique_ptr> = true; +#endif + /// @} group pointer_abstractions #if __cplusplus >= 201703L diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 2aab9426ecc..60400b3b8d2 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1568,6 +1568,14 @@ ftms = { }; }; +ftms = { + name = out_ptr; + values = { + v = 202106; + cxxmin = 23; + }; +}; + ftms = { name = spanstream; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 6829c70cf48..2e2af3e0c41 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -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 diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory index ac83761a26c..a526173b8cc 100644 --- a/libstdc++-v3/include/std/memory +++ b/libstdc++-v3/include/std/memory @@ -91,6 +91,10 @@ # include #endif +#if __cplusplus > 202002L +# include +#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 diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/1.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/1.cc new file mode 100644 index 00000000000..c42e5e13b75 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/1.cc @@ -0,0 +1,47 @@ +// { dg-do run { target c++23 } } + +#include +#include + +// 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; + +// 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 + +star_fish* star_fish_alloc() +{ + static std::uintptr_t counter = 1; + return reinterpret_cast(counter++); +} + +void star_fish_deleter::operator()(star_fish* c) const noexcept +{ + static std::uintptr_t counter = 1; + VERIFY(reinterpret_cast(c) == counter++); +} + +int star_fish_populate(star_fish** ps, const char*) +{ + VERIFY(ps); + star_fish_deleter()(*ps); + *ps = star_fish_alloc(); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/2.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/2.cc new file mode 100644 index 00000000000..ca6076209c2 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/2.cc @@ -0,0 +1,102 @@ +// { dg-do run { target c++23 } } + +#include +#include + +// 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 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 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 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 upbd(new Derived(1)); + { + auto iop = std::inout_ptr(upbd); + Base** bpp = iop; + VERIFY( static_cast(*bpp)->id == 1 ); + delete (Derived*)*bpp; + *bpp = new Derived(2); + } + VERIFY( upbd->id == 2 ); +} + +int main() +{ + test_unique_ptr(); +} diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc new file mode 100644 index 00000000000..b4ecf936105 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc @@ -0,0 +1,11 @@ +// { dg-do compile { target c++23 } } + +#include + +std::shared_ptr sp; +auto iop = std::inout_ptr(sp); // { dg-error "here" } +std::shared_ptr sp2; +std::default_delete d; +auto iop2 = std::inout_ptr(sp2, d); // { dg-error "here" } + +// { dg-error "can not be used" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc new file mode 100644 index 00000000000..4fce3df6b30 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc @@ -0,0 +1,35 @@ +// { dg-do compile { target c++23 } } + +#include +#include + +std::unique_ptr 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(intptr)); + VERIFY( *intptr == 999 ); +} + +int main() +{ + test_inout_ptr_void(); +} diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/1.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/1.cc new file mode 100644 index 00000000000..ed542a04fc9 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/1.cc @@ -0,0 +1,116 @@ +// { dg-do run { target c++23 } } +// { dg-require-fileio "" } +// { dg-additional-files "thirty_years_among_the_dead_preproc.txt" } + +#include +#include +#include + +// 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 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); +} + +// 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 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 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 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 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 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 + +int fopen_s(std::FILE** f, const char* name, const char* mode) +{ + if ((*f = std::fopen(name, mode))) + return 0; + return errno; +} diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/2.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/2.cc new file mode 100644 index 00000000000..77f952238eb --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/2.cc @@ -0,0 +1,273 @@ +// { dg-do compile { target c++23 } } + +#include +#include + +int counter = 0; + +void +test_unique_ptr() +{ + std::unique_ptr 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(cop) == static_cast(op) ); + } + VERIFY( *up == 2 ); + + { + std::default_delete 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(cop) == static_cast(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 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(cop) == static_cast(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(cop) == static_cast(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 upbd(new Derived(1)); + { + auto op = std::out_ptr(upbd); + Base** bpp = op; + VERIFY( *bpp == nullptr ); + *bpp = new Derived(2); + + const auto& cop = op; + VERIFY( static_cast(cop) == static_cast(op) ); + } + VERIFY( upbd->id == 2 ); +} + +void deleter_function(float* p) { delete p; } + +void +test_shared_ptr() +{ + using DD = std::default_delete; + + std::shared_ptr 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(cop) == static_cast(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 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(spd)->id == 22 ); + + struct Base { }; + struct Derived : Base + { + Derived(int id) : id(id) { } + int id; + }; + std::shared_ptr spbd(new Derived(1)); + { + auto op = std::out_ptr(spbd, std::default_delete()); + Base** bpp = op; + VERIFY( *bpp == nullptr ); + *bpp = new Derived(2); + } + VERIFY( spbd->id == 2 ); + + std::shared_ptr spf; + { + auto op = std::out_ptr(spf, deleter_function); + float** fpp = op; + *fpp = new float(0.5); + } + VERIFY( std::get_deleter(spf) != nullptr ); + VERIFY( *std::get_deleter(spf) == &deleter_function ); + VERIFY( *spf == 0.5 ); +} + +void +test_custom_ptr() +{ + struct UPtr : std::unique_ptr + { + using std::unique_ptr::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(cop) == static_cast(op) ); + } + VERIFY( *up == 2 ); + + { + auto op = std::out_ptr(up, std::default_delete{}); + 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 + { + using std::unique_ptr::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(cop) == static_cast(op) ); + } + VERIFY( *lp == 6 ); +} + +int main() +{ + test_unique_ptr(); + test_shared_ptr(); + test_custom_ptr(); + test_raw_ptr(); +} diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc new file mode 100644 index 00000000000..b2532dc7f72 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc @@ -0,0 +1,7 @@ +// { dg-do compile { target c++23 } } + +#include + +std::shared_ptr sp; +auto op = std::out_ptr(sp); // { dg-error "here" } +// { dg-error "deleter must be used" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc new file mode 100644 index 00000000000..3ff84a3aeaf --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc @@ -0,0 +1,60 @@ +// { dg-do compile { target c++23 } } + +#include +#include + +std::unique_ptr 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(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(intptr)); + VERIFY( *intptr == 999 ); +} + +int main() +{ + test_out_ptr_void(); + test_inout_ptr_void(); +} -- 2.41.0