diff options
Diffstat (limited to 'libstdc++-v3')
23 files changed, 2185 insertions, 373 deletions
diff --git a/libstdc++-v3/doc/doxygen/stdheader.cc b/libstdc++-v3/doc/doxygen/stdheader.cc index 3ee825f..839bfc8 100644 --- a/libstdc++-v3/doc/doxygen/stdheader.cc +++ b/libstdc++-v3/doc/doxygen/stdheader.cc @@ -54,7 +54,8 @@ void init_map() headers["function.h"] = "functional"; headers["functional_hash.h"] = "functional"; headers["mofunc_impl.h"] = "functional"; - headers["move_only_function.h"] = "functional"; + headers["cpyfunc_impl.h"] = "functional"; + headers["funcwrap.h"] = "functional"; headers["invoke.h"] = "functional"; headers["ranges_cmp.h"] = "functional"; headers["refwrap.h"] = "functional"; diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 1140fa0..3e5b6c4 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -194,6 +194,7 @@ bits_headers = \ ${bits_srcdir}/chrono_io.h \ ${bits_srcdir}/codecvt.h \ ${bits_srcdir}/cow_string.h \ + ${bits_srcdir}/cpyfunc_impl.h \ ${bits_srcdir}/deque.tcc \ ${bits_srcdir}/erase_if.h \ ${bits_srcdir}/formatfwd.h \ @@ -204,6 +205,7 @@ bits_headers = \ ${bits_srcdir}/fs_ops.h \ ${bits_srcdir}/fs_path.h \ ${bits_srcdir}/fstream.tcc \ + ${bits_srcdir}/funcwrap.h \ ${bits_srcdir}/gslice.h \ ${bits_srcdir}/gslice_array.h \ ${bits_srcdir}/hashtable.h \ @@ -223,7 +225,6 @@ bits_headers = \ ${bits_srcdir}/mask_array.h \ ${bits_srcdir}/memory_resource.h \ ${bits_srcdir}/mofunc_impl.h \ - ${bits_srcdir}/move_only_function.h \ ${bits_srcdir}/new_allocator.h \ ${bits_srcdir}/node_handle.h \ ${bits_srcdir}/ostream.tcc \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index c96e981..3531162 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -547,6 +547,7 @@ bits_freestanding = \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/chrono_io.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/codecvt.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/cow_string.h \ +@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/cpyfunc_impl.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/deque.tcc \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/erase_if.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/formatfwd.h \ @@ -557,6 +558,7 @@ bits_freestanding = \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/fs_ops.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/fs_path.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/fstream.tcc \ +@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/funcwrap.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/gslice.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/gslice_array.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/hashtable.h \ @@ -576,7 +578,6 @@ bits_freestanding = \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/mask_array.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/memory_resource.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/mofunc_impl.h \ -@GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/move_only_function.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/new_allocator.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/node_handle.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/ostream.tcc \ diff --git a/libstdc++-v3/include/bits/cpyfunc_impl.h b/libstdc++-v3/include/bits/cpyfunc_impl.h new file mode 100644 index 0000000..bc44cd3e --- /dev/null +++ b/libstdc++-v3/include/bits/cpyfunc_impl.h @@ -0,0 +1,269 @@ +// Implementation of std::copyable_function -*- 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/cpyfunc_impl.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{functional} + */ + +#ifndef _GLIBCXX_MOF_CV +# define _GLIBCXX_MOF_CV +#endif + +#ifdef _GLIBCXX_MOF_REF +# define _GLIBCXX_MOF_INV_QUALS _GLIBCXX_MOF_CV _GLIBCXX_MOF_REF +#else +# define _GLIBCXX_MOF_REF +# define _GLIBCXX_MOF_INV_QUALS _GLIBCXX_MOF_CV & +#endif + +#define _GLIBCXX_MOF_CV_REF _GLIBCXX_MOF_CV _GLIBCXX_MOF_REF + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + /** + * @brief Polymorphic copyable function wrapper. + * @ingroup functors + * @since C++26 + * @headerfile functional + * + * The `std::copyable_function` class template is a call wrapper similar + * to `std::function`, but it does not provide information about it's + * target, and preserves constness. + * + * It also supports const-qualification, ref-qualification, and + * no-throw guarantees. The qualifications and exception-specification + * of the `copyable_function::operator()` member function are respected + * when invoking the target function. + */ + template<typename _Res, typename... _ArgTypes, bool _Noex> + class copyable_function<_Res(_ArgTypes...) _GLIBCXX_MOF_CV + _GLIBCXX_MOF_REF noexcept(_Noex)> + : __polyfunc::_Cpy_base + { + using _Base = __polyfunc::_Cpy_base; + using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>; + using _Signature = _Invoker::_Signature; + + template<typename _Tp> + using __callable + = __conditional_t<_Noex, + is_nothrow_invocable_r<_Res, _Tp, _ArgTypes...>, + is_invocable_r<_Res, _Tp, _ArgTypes...>>; + + // [func.wrap.copy.con]/1 is-callable-from<VT> + template<typename _Vt> + static constexpr bool __is_callable_from + = __and_v<__callable<_Vt _GLIBCXX_MOF_CV_REF>, + __callable<_Vt _GLIBCXX_MOF_INV_QUALS>>; + + public: + using result_type = _Res; + + /// Creates an empty object. + copyable_function() noexcept { } + + /// Creates an empty object. + copyable_function(nullptr_t) noexcept { } + + /// Moves the target object, leaving the source empty. + copyable_function(copyable_function&& __x) noexcept + : _Base(static_cast<_Base&&>(__x)), + _M_invoke(std::__exchange(__x._M_invoke, nullptr)) + { } + + /// Copies the target object. + copyable_function(copyable_function const& __x) + : _Base(static_cast<const _Base&>(__x)), + _M_invoke(__x._M_invoke) + { } + + /// Stores a target object initialized from the argument. + template<typename _Fn, typename _Vt = decay_t<_Fn>> + requires (!is_same_v<_Vt, copyable_function>) + && (!__is_in_place_type_v<_Vt>) && __is_callable_from<_Vt> + copyable_function(_Fn&& __f) noexcept(_S_nothrow_init<_Vt, _Fn>()) + { + static_assert(is_copy_constructible_v<_Vt>); + if constexpr (is_function_v<remove_pointer_t<_Vt>> + || is_member_pointer_v<_Vt> + || __is_polymorphic_function_v<_Vt>) + { + if (__f == nullptr) + return; + } + + if constexpr (!__is_polymorphic_function_v<_Vt> + || !__polyfunc::__is_invoker_convertible<_Vt, copyable_function>()) + { + _M_init<_Vt>(std::forward<_Fn>(__f)); + _M_invoke = _Invoker::template _S_storage<_Vt _GLIBCXX_MOF_INV_QUALS>(); + } + else if constexpr (is_lvalue_reference_v<_Fn>) + { + _M_copy(__polyfunc::__base_of(__f)); + _M_invoke = __polyfunc::__invoker_of(__f); + } + else + { + _M_move(__polyfunc::__base_of(__f)); + _M_invoke = std::__exchange(__polyfunc::__invoker_of(__f), nullptr); + } + } + + /// Stores a target object initialized from the arguments. + template<typename _Tp, typename... _Args> + requires is_constructible_v<_Tp, _Args...> + && __is_callable_from<_Tp> + explicit + copyable_function(in_place_type_t<_Tp>, _Args&&... __args) + noexcept(_S_nothrow_init<_Tp, _Args...>()) + : _M_invoke(_Invoker::template _S_storage<_Tp _GLIBCXX_MOF_INV_QUALS>()) + { + static_assert(is_same_v<decay_t<_Tp>, _Tp>); + static_assert(is_copy_constructible_v<_Tp>); + _M_init<_Tp>(std::forward<_Args>(__args)...); + } + + /// Stores a target object initialized from the arguments. + template<typename _Tp, typename _Up, typename... _Args> + requires is_constructible_v<_Tp, initializer_list<_Up>&, _Args...> + && __is_callable_from<_Tp> + explicit + copyable_function(in_place_type_t<_Tp>, initializer_list<_Up> __il, + _Args&&... __args) + noexcept(_S_nothrow_init<_Tp, initializer_list<_Up>&, _Args...>()) + : _M_invoke(_Invoker::template _S_storage<_Tp _GLIBCXX_MOF_INV_QUALS>()) + { + static_assert(is_same_v<decay_t<_Tp>, _Tp>); + static_assert(is_copy_constructible_v<_Tp>); + _M_init<_Tp>(__il, std::forward<_Args>(__args)...); + } + + /// Stores a new target object, leaving `x` empty. + copyable_function& + operator=(copyable_function&& __x) noexcept + { + // Standard requires support of self assigment, by specifying it as + // copy and swap. + if (this != std::addressof(__x)) [[likely]] + { + _Base::operator=(static_cast<_Base&&>(__x)); + _M_invoke = std::__exchange(__x._M_invoke, nullptr); + } + return *this; + } + + /// Stores a copy of the source target object + copyable_function& + operator=(const copyable_function& __x) + { + copyable_function(__x).swap(*this); + return *this; + } + + /// Destroys the target object (if any). + copyable_function& + operator=(nullptr_t) noexcept + { + _M_reset(); + _M_invoke = nullptr; + return *this; + } + + /// Stores a new target object, initialized from the argument. + template<typename _Fn> + requires is_constructible_v<copyable_function, _Fn> + copyable_function& + operator=(_Fn&& __f) + noexcept(is_nothrow_constructible_v<copyable_function, _Fn>) + { + copyable_function(std::forward<_Fn>(__f)).swap(*this); + return *this; + } + + ~copyable_function() = default; + + /// True if a target object is present, false otherwise. + explicit operator bool() const noexcept + { return _M_invoke != nullptr; } + + /** Invoke the target object. + * + * The target object will be invoked using the supplied arguments, + * and as an lvalue or rvalue, and as const or non-const, as dictated + * by the template arguments of the `copyable_function` specialization. + * + * @pre Must not be empty. + */ + _Res + operator()(_ArgTypes... __args) _GLIBCXX_MOF_CV_REF noexcept(_Noex) + { + __glibcxx_assert(*this != nullptr); + return _M_invoke(this->_M_storage, std::forward<_ArgTypes>(__args)...); + } + + /// Exchange the target objects (if any). + void + swap(copyable_function& __x) noexcept + { + _Base::swap(__x); + std::swap(_M_invoke, __x._M_invoke); + } + + /// Exchange the target objects (if any). + friend void + swap(copyable_function& __x, copyable_function& __y) noexcept + { __x.swap(__y); } + + /// Check for emptiness by comparing with `nullptr`. + friend bool + operator==(const copyable_function& __x, nullptr_t) noexcept + { return __x._M_invoke == nullptr; } + + private: + typename _Invoker::__storage_func_t _M_invoke = nullptr; + + template<typename _Func> + friend auto& + __polyfunc::__invoker_of(_Func&) noexcept; + + template<typename _Func> + friend auto& + __polyfunc::__base_of(_Func&) noexcept; + + template<typename _Dst, typename _Src> + friend consteval bool + __polyfunc::__is_invoker_convertible() noexcept; + }; + +#undef _GLIBCXX_MOF_CV_REF +#undef _GLIBCXX_MOF_CV +#undef _GLIBCXX_MOF_REF +#undef _GLIBCXX_MOF_INV_QUALS + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std diff --git a/libstdc++-v3/include/bits/funcwrap.h b/libstdc++-v3/include/bits/funcwrap.h new file mode 100644 index 0000000..4e05353 --- /dev/null +++ b/libstdc++-v3/include/bits/funcwrap.h @@ -0,0 +1,507 @@ +// Implementation of std::move_only_function and std::copyable_function -*- 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/funcwrap.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{functional} + */ + +#ifndef _GLIBCXX_FUNCWRAP_H +#define _GLIBCXX_FUNCWRAP_H 1 + +#ifdef _GLIBCXX_SYSHDR +#pragma GCC system_header +#endif + +#include <bits/version.h> + +#if defined(__glibcxx_move_only_function) || defined(__glibcxx_copyable_function) + +#include <bits/invoke.h> +#include <bits/utility.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + /// @cond undocumented + template<typename _Tp> + inline constexpr bool __is_polymorphic_function_v = false; + + namespace __polyfunc + { + union _Ptrs + { + void* _M_obj; + void (*_M_func)(); + }; + + struct _Storage + { + void* _M_addr() noexcept { return &_M_bytes[0]; } + void const* _M_addr() const noexcept { return &_M_bytes[0]; } + + template<typename _Tp> + static consteval bool + _S_stored_locally() noexcept + { + return sizeof(_Tp) <= sizeof(_Storage) + && alignof(_Tp) <= alignof(_Storage) + && is_nothrow_move_constructible_v<_Tp>; + } + + template<typename _Tp, typename... _Args> + static consteval bool + _S_nothrow_init() noexcept + { + if constexpr (_S_stored_locally<_Tp>()) + return is_nothrow_constructible_v<_Tp, _Args...>; + return false; + } + + template<typename _Tp, typename... _Args> + void + _M_init(_Args&&... __args) noexcept(_S_nothrow_init<_Tp, _Args...>()) + { + if constexpr (is_function_v<remove_pointer_t<_Tp>>) + { + static_assert( sizeof...(__args) <= 1 ); + // __args can have up to one element, returns nullptr if empty. + _Tp __func = (nullptr, ..., __args); + _M_ptrs._M_func = reinterpret_cast<void(*)()>(__func); + } + else if constexpr (!_S_stored_locally<_Tp>()) + _M_ptrs._M_obj = new _Tp(std::forward<_Args>(__args)...); + else + ::new (_M_addr()) _Tp(std::forward<_Args>(__args)...); + } + + template<typename _Tp> + [[__gnu__::__always_inline__]] + _Tp* + _M_ptr() const noexcept + { + if constexpr (!_S_stored_locally<remove_const_t<_Tp>>()) + return static_cast<_Tp*>(_M_ptrs._M_obj); + else if constexpr (is_const_v<_Tp>) + return static_cast<_Tp*>(_M_addr()); + else + // _Manager and _Invoker pass _Storage by const&, even for mutable sources. + return static_cast<_Tp*>(const_cast<void*>(_M_addr())); + } + + template<typename _Ref> + [[__gnu__::__always_inline__]] + _Ref + _M_ref() const noexcept + { + using _Tp = remove_reference_t<_Ref>; + if constexpr (is_function_v<remove_pointer_t<_Tp>>) + return reinterpret_cast<_Tp>(_M_ptrs._M_func); + else + return static_cast<_Ref>(*_M_ptr<_Tp>()); + } + + // We want to have enough space to store a simple delegate type. + struct _Delegate { void (_Storage::*__pfm)(); _Storage* __obj; }; + union { + _Ptrs _M_ptrs; + alignas(_Delegate) alignas(void(*)()) + unsigned char _M_bytes[sizeof(_Delegate)]; + }; + }; + + template<bool _Noex, typename _Ret, typename... _Args> + struct _Base_invoker + { + using _Signature = _Ret(*)(_Args...) noexcept(_Noex); + + using __storage_func_t = _Ret(*)(const _Storage&, _Args...) noexcept(_Noex); + template<typename _Tp> + static consteval __storage_func_t + _S_storage() + { return &_S_call_storage<_Adjust_target<_Tp>>; } + + private: + template<typename _Tp, typename _Td = remove_cvref_t<_Tp>> + using _Adjust_target = + __conditional_t<is_pointer_v<_Td> || is_member_pointer_v<_Td>, _Td, _Tp>; + + template<typename _Tp> + static _Ret + _S_call_storage(const _Storage& __ref, _Args... __args) noexcept(_Noex) + { + return std::__invoke_r<_Ret>(__ref._M_ref<_Tp>(), + std::forward<_Args>(__args)...); + } + }; + + template<typename _Tp> + using __param_t = __conditional_t<is_scalar_v<_Tp>, _Tp, _Tp&&>; + + template<bool _Noex, typename _Ret, typename... _Args> + using _Invoker = _Base_invoker<_Noex, remove_cv_t<_Ret>, __param_t<_Args>...>; + + template<typename _Func> + auto& + __invoker_of(_Func& __f) noexcept + { return __f._M_invoke; } + + template<typename _Func> + auto& + __base_of(_Func& __f) noexcept + { return static_cast<__like_t<_Func&, typename _Func::_Base>>(__f); } + + template<typename _Src, typename _Dst> + consteval bool + __is_invoker_convertible() noexcept + { + if constexpr (requires { typename _Src::_Signature; }) + return is_convertible_v<typename _Src::_Signature, + typename _Dst::_Signature>; + else + return false; + } + + struct _Manager + { + enum class _Op + { + // saves address of entity in *__src to __target._M_ptrs, + _Address, + // moves entity stored in *__src to __target, __src becomes empty + _Move, + // copies entity stored in *__src to __target, supported only if + // _ProvideCopy is specified. + _Copy, + // destroys entity stored in __target, __src is ignoring + _Destroy, + }; + + // A function that performs operation __op on the __target and possibly __src. + using _Func = void (*)(_Op __op, _Storage& __target, const _Storage* __src); + + // The no-op manager function for objects with no target. + static void _S_empty(_Op, _Storage&, const _Storage*) noexcept { } + + template<bool _ProvideCopy, typename _Tp> + consteval static auto + _S_select() + { + if constexpr (is_function_v<remove_pointer_t<_Tp>>) + return &_S_func; + else if constexpr (!_Storage::_S_stored_locally<_Tp>()) + return &_S_ptr<_ProvideCopy, _Tp>; + else if constexpr (is_trivially_copyable_v<_Tp>) + return &_S_trivial; + else + return &_S_local<_ProvideCopy, _Tp>; + } + + private: + static void + _S_func(_Op __op, _Storage& __target, const _Storage* __src) noexcept + { + switch (__op) + { + case _Op::_Address: + case _Op::_Move: + case _Op::_Copy: + __target._M_ptrs._M_func = __src->_M_ptrs._M_func; + return; + case _Op::_Destroy: + return; + } + } + + static void + _S_trivial(_Op __op, _Storage& __target, const _Storage* __src) noexcept + { + switch (__op) + { + case _Op::_Address: + __target._M_ptrs._M_obj = const_cast<void*>(__src->_M_addr()); + return; + case _Op::_Move: + case _Op::_Copy: + // N.B. Creating _Storage starts lifetime of _M_bytes char array, + // that implicitly creates, amongst other, all possible trivially + // copyable objects, so we copy any object present in __src._M_bytes. + ::new (&__target) _Storage(*__src); + return; + case _Op::_Destroy: + return; + } + } + + template<bool _Provide_copy, typename _Tp> + static void + _S_local(_Op __op, _Storage& __target, const _Storage* __src) + noexcept(!_Provide_copy) + { + switch (__op) + { + case _Op::_Address: + __target._M_ptrs._M_obj = __src->_M_ptr<_Tp>(); + return; + case _Op::_Move: + { + _Tp* __obj = __src->_M_ptr<_Tp>(); + ::new(__target._M_addr()) _Tp(std::move(*__obj)); + __obj->~_Tp(); + } + return; + case _Op::_Destroy: + __target._M_ptr<_Tp>()->~_Tp(); + return; + case _Op::_Copy: + if constexpr (_Provide_copy) + ::new (__target._M_addr()) _Tp(__src->_M_ref<const _Tp&>()); + else + __builtin_unreachable(); + return; + } + } + + template<bool _Provide_copy, typename _Tp> + static void + _S_ptr(_Op __op, _Storage& __target, const _Storage* __src) + noexcept(!_Provide_copy) + { + switch (__op) + { + case _Op::_Address: + case _Op::_Move: + __target._M_ptrs._M_obj = __src->_M_ptrs._M_obj; + return; + case _Op::_Destroy: + delete __target._M_ptr<_Tp>(); + return; + case _Op::_Copy: + if constexpr (_Provide_copy) + __target._M_ptrs._M_obj = new _Tp(__src->_M_ref<const _Tp&>()); + else + __builtin_unreachable(); + return; + } + } + }; + + class _Mo_base + { + protected: + _Mo_base() noexcept + : _M_manage(_Manager::_S_empty) + { } + + _Mo_base(_Mo_base&& __x) noexcept + { _M_move(__x); } + + template<typename _Tp, typename... _Args> + static consteval bool + _S_nothrow_init() noexcept + { return _Storage::_S_nothrow_init<_Tp, _Args...>(); } + + template<typename _Tp, typename... _Args> + void + _M_init(_Args&&... __args) + noexcept(_S_nothrow_init<_Tp, _Args...>()) + { + _M_storage._M_init<_Tp>(std::forward<_Args>(__args)...); + _M_manage = _Manager::_S_select<false, _Tp>(); + } + + void + _M_move(_Mo_base& __x) noexcept + { + using _Op = _Manager::_Op; + _M_manage = std::__exchange(__x._M_manage, _Manager::_S_empty); + _M_manage(_Op::_Move, _M_storage, &__x._M_storage); + } + + _Mo_base& + operator=(_Mo_base&& __x) noexcept + { + _M_destroy(); + _M_move(__x); + return *this; + } + + void + _M_reset() noexcept + { + _M_destroy(); + _M_manage = _Manager::_S_empty; + } + + ~_Mo_base() + { _M_destroy(); } + + void + swap(_Mo_base& __x) noexcept + { + using _Op = _Manager::_Op; + // Order of operations here is more efficient if __x is empty. + _Storage __s; + __x._M_manage(_Op::_Move, __s, &__x._M_storage); + _M_manage(_Op::_Move, __x._M_storage, &_M_storage); + __x._M_manage(_Op::_Move, _M_storage, &__s); + std::swap(_M_manage, __x._M_manage); + } + + _Storage _M_storage; + + private: + void _M_destroy() noexcept + { _M_manage(_Manager::_Op::_Destroy, _M_storage, nullptr); } + + _Manager::_Func _M_manage; + +#ifdef __glibcxx_copyable_function // C++ >= 26 && HOSTED + friend class _Cpy_base; +#endif // __glibcxx_copyable_function + }; + +} // namespace __polyfunc + /// @endcond + +#ifdef __glibcxx_move_only_function // C++ >= 23 && HOSTED + template<typename... _Signature> + class move_only_function; // not defined + + /// @cond undocumented + template<typename _Tp> + constexpr bool __is_polymorphic_function_v<move_only_function<_Tp>> = true; + + namespace __detail::__variant + { + template<typename> struct _Never_valueless_alt; // see <variant> + + // Provide the strong exception-safety guarantee when emplacing a + // move_only_function into a variant. + template<typename... _Signature> + struct _Never_valueless_alt<std::move_only_function<_Signature...>> + : true_type + { }; + } // namespace __detail::__variant + /// @endcond +#endif // __glibcxx_move_only_function + +#ifdef __glibcxx_copyable_function // C++ >= 26 && HOSTED + /// @cond undocumented + namespace __polyfunc + { + class _Cpy_base : public _Mo_base + { + protected: + _Cpy_base() = default; + + template<typename _Tp, typename... _Args> + void + _M_init(_Args&&... __args) + noexcept(_S_nothrow_init<_Tp, _Args...>()) + { + _M_storage._M_init<_Tp>(std::forward<_Args>(__args)...); + _M_manage = _Manager::_S_select<true, _Tp>(); + } + + void + _M_copy(_Cpy_base const& __x) + { + using _Op = _Manager::_Op; + __x._M_manage(_Op::_Copy, _M_storage, &__x._M_storage); + _M_manage = __x._M_manage; + } + + _Cpy_base(_Cpy_base&&) = default; + + _Cpy_base(_Cpy_base const& __x) + { _M_copy(__x); } + + _Cpy_base& + operator=(_Cpy_base&&) = default; + + _Cpy_base& + // Needs to use copy and swap for exception guarantees. + operator=(_Cpy_base const&) = delete; + }; + } // namespace __polyfunc + /// @endcond + + template<typename... _Signature> + class copyable_function; // not defined + + template<typename _Tp> + constexpr bool __is_polymorphic_function_v<copyable_function<_Tp>> = true; + + namespace __detail::__variant + { + template<typename> struct _Never_valueless_alt; // see <variant> + + // Provide the strong exception-safety guarantee when emplacing a + // copyable_function into a variant. + template<typename... _Signature> + struct _Never_valueless_alt<std::copyable_function<_Signature...>> + : true_type + { }; + } // namespace __detail::__variant +#endif // __glibcxx_copyable_function + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#ifdef __glibcxx_move_only_function // C++ >= 23 && HOSTED +#include "mofunc_impl.h" +#define _GLIBCXX_MOF_CV const +#include "mofunc_impl.h" +#define _GLIBCXX_MOF_REF & +#include "mofunc_impl.h" +#define _GLIBCXX_MOF_REF && +#include "mofunc_impl.h" +#define _GLIBCXX_MOF_CV const +#define _GLIBCXX_MOF_REF & +#include "mofunc_impl.h" +#define _GLIBCXX_MOF_CV const +#define _GLIBCXX_MOF_REF && +#include "mofunc_impl.h" +#endif // __glibcxx_move_only_function + +#ifdef __glibcxx_copyable_function // C++ >= 26 && HOSTED +#include "cpyfunc_impl.h" +#define _GLIBCXX_MOF_CV const +#include "cpyfunc_impl.h" +#define _GLIBCXX_MOF_REF & +#include "cpyfunc_impl.h" +#define _GLIBCXX_MOF_REF && +#include "cpyfunc_impl.h" +#define _GLIBCXX_MOF_CV const +#define _GLIBCXX_MOF_REF & +#include "cpyfunc_impl.h" +#define _GLIBCXX_MOF_CV const +#define _GLIBCXX_MOF_REF && +#include "cpyfunc_impl.h" +#endif // __glibcxx_copyable_function + +#endif // __glibcxx_copyable_function || __glibcxx_copyable_function +#endif // _GLIBCXX_FUNCWRAP_H diff --git a/libstdc++-v3/include/bits/mofunc_impl.h b/libstdc++-v3/include/bits/mofunc_impl.h index 318a55e..1ceb910 100644 --- a/libstdc++-v3/include/bits/mofunc_impl.h +++ b/libstdc++-v3/include/bits/mofunc_impl.h @@ -62,8 +62,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Res, typename... _ArgTypes, bool _Noex> class move_only_function<_Res(_ArgTypes...) _GLIBCXX_MOF_CV _GLIBCXX_MOF_REF noexcept(_Noex)> - : _Mofunc_base + : __polyfunc::_Mo_base { + using _Base = __polyfunc::_Mo_base; + using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>; + using _Signature = _Invoker::_Signature; + template<typename _Tp> using __callable = __conditional_t<_Noex, @@ -87,7 +91,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Moves the target object, leaving the source empty. move_only_function(move_only_function&& __x) noexcept - : _Mofunc_base(static_cast<_Mofunc_base&&>(__x)), + : _Base(static_cast<_Base&&>(__x)), _M_invoke(std::__exchange(__x._M_invoke, nullptr)) { } @@ -97,15 +101,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION && (!__is_in_place_type_v<_Vt>) && __is_callable_from<_Vt> move_only_function(_Fn&& __f) noexcept(_S_nothrow_init<_Vt, _Fn>()) { + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4255. move_only_function constructor should recognize empty + // copyable_functions if constexpr (is_function_v<remove_pointer_t<_Vt>> || is_member_pointer_v<_Vt> - || __is_move_only_function_v<_Vt>) + || __is_polymorphic_function_v<_Vt>) { if (__f == nullptr) return; } - _M_init<_Vt>(std::forward<_Fn>(__f)); - _M_invoke = &_S_invoke<_Vt>; + + if constexpr (__is_polymorphic_function_v<_Vt> + && __polyfunc::__is_invoker_convertible<_Vt, move_only_function>()) + { + // Handle cases where _Fn is const reference to copyable_function, + // by firstly creating temporary and moving from it. + _Vt __tmp(std::forward<_Fn>(__f)); + _M_move(__polyfunc::__base_of(__tmp)); + _M_invoke = std::__exchange(__polyfunc::__invoker_of(__tmp), nullptr); + } + else + { + _M_init<_Vt>(std::forward<_Fn>(__f)); + _M_invoke = _Invoker::template _S_storage<_Vt _GLIBCXX_MOF_INV_QUALS>(); + } } /// Stores a target object initialized from the arguments. @@ -115,7 +135,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION explicit move_only_function(in_place_type_t<_Tp>, _Args&&... __args) noexcept(_S_nothrow_init<_Tp, _Args...>()) - : _M_invoke(&_S_invoke<_Tp>) + : _M_invoke(_Invoker::template _S_storage<_Tp _GLIBCXX_MOF_INV_QUALS>()) { static_assert(is_same_v<decay_t<_Tp>, _Tp>); _M_init<_Tp>(std::forward<_Args>(__args)...); @@ -129,7 +149,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION move_only_function(in_place_type_t<_Tp>, initializer_list<_Up> __il, _Args&&... __args) noexcept(_S_nothrow_init<_Tp, initializer_list<_Up>&, _Args...>()) - : _M_invoke(&_S_invoke<_Tp>) + : _M_invoke(_Invoker::template _S_storage<_Tp _GLIBCXX_MOF_INV_QUALS>()) { static_assert(is_same_v<decay_t<_Tp>, _Tp>); _M_init<_Tp>(__il, std::forward<_Args>(__args)...); @@ -139,8 +159,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION move_only_function& operator=(move_only_function&& __x) noexcept { - _Mofunc_base::operator=(static_cast<_Mofunc_base&&>(__x)); - _M_invoke = std::__exchange(__x._M_invoke, nullptr); + // Standard requires support of self assigment, by specifying it as + // copy and swap. + if (this != std::addressof(__x)) [[likely]] + { + _Base::operator=(static_cast<_Base&&>(__x)); + _M_invoke = std::__exchange(__x._M_invoke, nullptr); + } return *this; } @@ -148,7 +173,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION move_only_function& operator=(nullptr_t) noexcept { - _Mofunc_base::operator=(nullptr); + _M_reset(); _M_invoke = nullptr; return *this; } @@ -167,7 +192,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ~move_only_function() = default; /// True if a target object is present, false otherwise. - explicit operator bool() const noexcept { return _M_invoke != nullptr; } + explicit operator bool() const noexcept + { return _M_invoke != nullptr; } /** Invoke the target object. * @@ -181,14 +207,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator()(_ArgTypes... __args) _GLIBCXX_MOF_CV_REF noexcept(_Noex) { __glibcxx_assert(*this != nullptr); - return _M_invoke(this, std::forward<_ArgTypes>(__args)...); + return _M_invoke(this->_M_storage, std::forward<_ArgTypes>(__args)...); } /// Exchange the target objects (if any). void swap(move_only_function& __x) noexcept { - _Mofunc_base::swap(__x); + _Base::swap(__x); std::swap(_M_invoke, __x._M_invoke); } @@ -203,25 +229,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return __x._M_invoke == nullptr; } private: - template<typename _Tp> - using __param_t = __conditional_t<is_scalar_v<_Tp>, _Tp, _Tp&&>; + typename _Invoker::__storage_func_t _M_invoke = nullptr; - using _Invoker = _Res (*)(_Mofunc_base _GLIBCXX_MOF_CV*, - __param_t<_ArgTypes>...) noexcept(_Noex); + template<typename _Func> + friend auto& + __polyfunc::__invoker_of(_Func&) noexcept; - template<typename _Tp> - static _Res - _S_invoke(_Mofunc_base _GLIBCXX_MOF_CV* __self, - __param_t<_ArgTypes>... __args) noexcept(_Noex) - { - using _TpCv = _Tp _GLIBCXX_MOF_CV; - using _TpInv = _Tp _GLIBCXX_MOF_INV_QUALS; - return std::__invoke_r<_Res>( - std::forward<_TpInv>(*_S_access<_TpCv>(__self)), - std::forward<__param_t<_ArgTypes>>(__args)...); - } + template<typename _Func> + friend auto& + __polyfunc::__base_of(_Func&) noexcept; - _Invoker _M_invoke = nullptr; + template<typename _Dst, typename _Src> + friend consteval bool + __polyfunc::__is_invoker_convertible() noexcept; }; #undef _GLIBCXX_MOF_CV_REF diff --git a/libstdc++-v3/include/bits/move_only_function.h b/libstdc++-v3/include/bits/move_only_function.h deleted file mode 100644 index 42b33d0..0000000 --- a/libstdc++-v3/include/bits/move_only_function.h +++ /dev/null @@ -1,218 +0,0 @@ -// Implementation of std::move_only_function -*- 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/move_only_function.h - * This is an internal header file, included by other library headers. - * Do not attempt to use it directly. @headername{functional} - */ - -#ifndef _GLIBCXX_MOVE_ONLY_FUNCTION_H -#define _GLIBCXX_MOVE_ONLY_FUNCTION_H 1 - -#ifdef _GLIBCXX_SYSHDR -#pragma GCC system_header -#endif - -#include <bits/version.h> - -#ifdef __glibcxx_move_only_function // C++ >= 23 && HOSTED - -#include <bits/invoke.h> -#include <bits/utility.h> - -namespace std _GLIBCXX_VISIBILITY(default) -{ -_GLIBCXX_BEGIN_NAMESPACE_VERSION - - template<typename... _Signature> - class move_only_function; // not defined - - /// @cond undocumented - class _Mofunc_base - { - protected: - _Mofunc_base() noexcept - : _M_manage(_S_empty) - { } - - _Mofunc_base(_Mofunc_base&& __x) noexcept - { - _M_manage = std::__exchange(__x._M_manage, _S_empty); - _M_manage(_M_storage, &__x._M_storage); - } - - template<typename _Tp, typename... _Args> - static constexpr bool - _S_nothrow_init() noexcept - { - if constexpr (__stored_locally<_Tp>) - return is_nothrow_constructible_v<_Tp, _Args...>; - return false; - } - - template<typename _Tp, typename... _Args> - void - _M_init(_Args&&... __args) noexcept(_S_nothrow_init<_Tp, _Args...>()) - { - if constexpr (__stored_locally<_Tp>) - ::new (_M_storage._M_addr()) _Tp(std::forward<_Args>(__args)...); - else - _M_storage._M_p = new _Tp(std::forward<_Args>(__args)...); - - _M_manage = &_S_manage<_Tp>; - } - - _Mofunc_base& - operator=(_Mofunc_base&& __x) noexcept - { - _M_manage(_M_storage, nullptr); - _M_manage = std::__exchange(__x._M_manage, _S_empty); - _M_manage(_M_storage, &__x._M_storage); - return *this; - } - - _Mofunc_base& - operator=(nullptr_t) noexcept - { - _M_manage(_M_storage, nullptr); - _M_manage = _S_empty; - return *this; - } - - ~_Mofunc_base() { _M_manage(_M_storage, nullptr); } - - void - swap(_Mofunc_base& __x) noexcept - { - // Order of operations here is more efficient if __x is empty. - _Storage __s; - __x._M_manage(__s, &__x._M_storage); - _M_manage(__x._M_storage, &_M_storage); - __x._M_manage(_M_storage, &__s); - std::swap(_M_manage, __x._M_manage); - } - - template<typename _Tp, typename _Self> - static _Tp* - _S_access(_Self* __self) noexcept - { - if constexpr (__stored_locally<remove_const_t<_Tp>>) - return static_cast<_Tp*>(__self->_M_storage._M_addr()); - else - return static_cast<_Tp*>(__self->_M_storage._M_p); - } - - private: - struct _Storage - { - void* _M_addr() noexcept { return &_M_bytes[0]; } - const void* _M_addr() const noexcept { return &_M_bytes[0]; } - - // We want to have enough space to store a simple delegate type. - struct _Delegate { void (_Storage::*__pfm)(); _Storage* __obj; }; - union { - void* _M_p; - alignas(_Delegate) alignas(void(*)()) - unsigned char _M_bytes[sizeof(_Delegate)]; - }; - }; - - template<typename _Tp> - static constexpr bool __stored_locally - = sizeof(_Tp) <= sizeof(_Storage) && alignof(_Tp) <= alignof(_Storage) - && is_nothrow_move_constructible_v<_Tp>; - - // A function that either destroys the target object stored in __target, - // or moves the target object from *__src to __target. - using _Manager = void (*)(_Storage& __target, _Storage* __src) noexcept; - - // The no-op manager function for objects with no target. - static void _S_empty(_Storage&, _Storage*) noexcept { } - - // The real manager function for a target object of type _Tp. - template<typename _Tp> - static void - _S_manage(_Storage& __target, _Storage* __src) noexcept - { - if constexpr (__stored_locally<_Tp>) - { - if (__src) - { - _Tp* __rval = static_cast<_Tp*>(__src->_M_addr()); - ::new (__target._M_addr()) _Tp(std::move(*__rval)); - __rval->~_Tp(); - } - else - static_cast<_Tp*>(__target._M_addr())->~_Tp(); - } - else - { - if (__src) - __target._M_p = __src->_M_p; - else - delete static_cast<_Tp*>(__target._M_p); - } - } - - _Storage _M_storage; - _Manager _M_manage; - }; - - template<typename _Tp> - inline constexpr bool __is_move_only_function_v = false; - template<typename _Tp> - constexpr bool __is_move_only_function_v<move_only_function<_Tp>> = true; - /// @endcond - - namespace __detail::__variant - { - template<typename> struct _Never_valueless_alt; // see <variant> - - // Provide the strong exception-safety guarantee when emplacing a - // move_only_function into a variant. - template<typename... _Signature> - struct _Never_valueless_alt<std::move_only_function<_Signature...>> - : true_type - { }; - } // namespace __detail::__variant - -_GLIBCXX_END_NAMESPACE_VERSION -} // namespace std - -#include "mofunc_impl.h" -#define _GLIBCXX_MOF_CV const -#include "mofunc_impl.h" -#define _GLIBCXX_MOF_REF & -#include "mofunc_impl.h" -#define _GLIBCXX_MOF_REF && -#include "mofunc_impl.h" -#define _GLIBCXX_MOF_CV const -#define _GLIBCXX_MOF_REF & -#include "mofunc_impl.h" -#define _GLIBCXX_MOF_CV const -#define _GLIBCXX_MOF_REF && -#include "mofunc_impl.h" - -#endif // __glibcxx_move_only_function -#endif // _GLIBCXX_MOVE_ONLY_FUNCTION_H diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 2d34a8d..6ca148f 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1748,6 +1748,15 @@ ftms = { }; ftms = { + name = copyable_function; + values = { + v = 202306; + cxxmin = 26; + hosted = yes; + }; +}; + +ftms = { name = out_ptr; values = { v = 202311; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 24831f7..48a090c 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1948,6 +1948,16 @@ #endif /* !defined(__cpp_lib_move_only_function) && defined(__glibcxx_want_move_only_function) */ #undef __glibcxx_want_move_only_function +#if !defined(__cpp_lib_copyable_function) +# if (__cplusplus > 202302L) && _GLIBCXX_HOSTED +# define __glibcxx_copyable_function 202306L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_copyable_function) +# define __cpp_lib_copyable_function 202306L +# endif +# endif +#endif /* !defined(__cpp_lib_copyable_function) && defined(__glibcxx_want_copyable_function) */ +#undef __glibcxx_want_copyable_function + #if !defined(__cpp_lib_out_ptr) # if (__cplusplus >= 202100L) # define __glibcxx_out_ptr 202311L diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index b3192cf..f0b0252 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -1863,20 +1863,24 @@ namespace __format _Spec<_CharT> _M_spec{}; }; +#ifdef __BFLT16_DIG__ + using __bflt16_t = decltype(0.0bf16); +#endif + // Decide how 128-bit floating-point types should be formatted (or not). - // When supported, the typedef __format::__float128_t is the type that - // format arguments should be converted to for storage in basic_format_arg. + // When supported, the typedef __format::__flt128_t is the type that format + // arguments should be converted to before passing them to __formatter_fp. // Define the macro _GLIBCXX_FORMAT_F128 to say they're supported. - // _GLIBCXX_FORMAT_F128=1 means __float128, _Float128 etc. will be formatted - // by converting them to long double (or __ieee128 for powerpc64le). - // _GLIBCXX_FORMAT_F128=2 means basic_format_arg needs to enable explicit - // support for _Float128, rather than formatting it as another type. + // The __float128, _Float128 will be formatted by converting them to: + // __ieee128 (same as __float128) when _GLIBCXX_FORMAT_F128=1, + // long double when _GLIBCXX_FORMAT_F128=2, + // _Float128 when _GLIBCXX_FORMAT_F128=3. #undef _GLIBCXX_FORMAT_F128 #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT // Format 128-bit floating-point types using __ieee128. - using __float128_t = __ieee128; + using __flt128_t = __ieee128; # define _GLIBCXX_FORMAT_F128 1 #ifdef __LONG_DOUBLE_IEEE128__ @@ -1910,14 +1914,14 @@ namespace __format #elif defined _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128 // Format 128-bit floating-point types using long double. - using __float128_t = long double; -# define _GLIBCXX_FORMAT_F128 1 + using __flt128_t = long double; +# define _GLIBCXX_FORMAT_F128 2 #elif __FLT128_DIG__ && defined(_GLIBCXX_HAVE_FLOAT128_MATH) // Format 128-bit floating-point types using _Float128. - using __float128_t = _Float128; -# define _GLIBCXX_FORMAT_F128 2 + using __flt128_t = _Float128; +# define _GLIBCXX_FORMAT_F128 3 # if __cplusplus == 202002L // These overloads exist in the library, but are not declared for C++20. @@ -2947,8 +2951,8 @@ namespace __format }; #endif -#if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128 == 1 - // Reuse __formatter_fp<C>::format<__float128_t, Out> for _Float128. +#if defined(__FLT128_DIG__) && _GLIBCXX_FORMAT_F128 + // Use __formatter_fp<C>::format<__format::__flt128_t, Out> for _Float128. template<__format::__char _CharT> struct formatter<_Float128, _CharT> { @@ -2962,17 +2966,45 @@ namespace __format template<typename _Out> typename basic_format_context<_Out, _CharT>::iterator format(_Float128 __u, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f.format((__format::__float128_t)__u, __fc); } + { return _M_f.format((__format::__flt128_t)__u, __fc); } + + private: + __format::__formatter_fp<_CharT> _M_f; + }; +#endif + +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 != 1 + // Reuse __formatter_fp<C>::format<__format::__flt128_t, Out> for __float128. + // This formatter is not declared if _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT is true, + // as __float128 when present is same type as __ieee128, which may be same as + // long double. + template<__format::__char _CharT> + struct formatter<__float128, _CharT> + { + formatter() = default; + + [[__gnu__::__always_inline__]] + constexpr typename basic_format_parse_context<_CharT>::iterator + parse(basic_format_parse_context<_CharT>& __pc) + { return _M_f.parse(__pc); } + + template<typename _Out> + typename basic_format_context<_Out, _CharT>::iterator + format(__float128 __u, basic_format_context<_Out, _CharT>& __fc) const + { return _M_f.format((__format::__flt128_t)__u, __fc); } private: __format::__formatter_fp<_CharT> _M_f; + + static_assert( !is_same_v<__float128, long double>, + "This specialization should not be used for long double" ); }; #endif #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) // Reuse __formatter_fp<C>::format<float, Out> for bfloat16_t. template<__format::__char _CharT> - struct formatter<__gnu_cxx::__bfloat16_t, _CharT> + struct formatter<__format::__bflt16_t, _CharT> { formatter() = default; @@ -3835,16 +3867,14 @@ namespace __format enum _Arg_t : unsigned char { _Arg_none, _Arg_bool, _Arg_c, _Arg_i, _Arg_u, _Arg_ll, _Arg_ull, _Arg_flt, _Arg_dbl, _Arg_ldbl, _Arg_str, _Arg_sv, _Arg_ptr, _Arg_handle, - _Arg_i128, _Arg_u128, - _Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64, // These are unused. + _Arg_i128, _Arg_u128, _Arg_float128, + _Arg_bf16, _Arg_f16, _Arg_f32, _Arg_f64, + _Arg_max_, + #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT - _Arg_next_value_, - _Arg_f128 = _Arg_ldbl, - _Arg_ibm128 = _Arg_next_value_, -#else - _Arg_f128, + _Arg_ibm128 = _Arg_ldbl, + _Arg_ieee128 = _Arg_float128, #endif - _Arg_max_ }; template<typename _Context> @@ -3871,6 +3901,12 @@ namespace __format double _M_dbl; #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT // No long double if it's ambiguous. long double _M_ldbl; +#else + __ibm128 _M_ibm128; + __ieee128 _M_ieee128; +#endif +#ifdef __SIZEOF_FLOAT128__ + __float128 _M_float128; #endif const _CharT* _M_str; basic_string_view<_CharT> _M_sv; @@ -3880,11 +3916,17 @@ namespace __format __int128 _M_i128; unsigned __int128 _M_u128; #endif -#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT - __ieee128 _M_f128; - __ibm128 _M_ibm128; -#elif _GLIBCXX_FORMAT_F128 == 2 - __float128_t _M_f128; +#ifdef __BFLT16_DIG__ + __bflt16_t _M_bf16; +#endif +#ifdef __FLT16_DIG__ + _Float16 _M_f16; +#endif +#ifdef __FLT32_DIG__ + _Float32 _M_f32; +#endif +#ifdef __FLT64_DIG__ + _Float64 _M_f64; #endif }; @@ -3922,10 +3964,14 @@ namespace __format else if constexpr (is_same_v<_Tp, long double>) return __u._M_ldbl; #else - else if constexpr (is_same_v<_Tp, __ieee128>) - return __u._M_f128; else if constexpr (is_same_v<_Tp, __ibm128>) return __u._M_ibm128; + else if constexpr (is_same_v<_Tp, __ieee128>) + return __u._M_ieee128; +#endif +#ifdef __SIZEOF_FLOAT128__ + else if constexpr (is_same_v<_Tp, __float128>) + return __u._M_float128; #endif else if constexpr (is_same_v<_Tp, const _CharT*>) return __u._M_str; @@ -3939,9 +3985,21 @@ namespace __format else if constexpr (is_same_v<_Tp, unsigned __int128>) return __u._M_u128; #endif -#if _GLIBCXX_FORMAT_F128 == 2 - else if constexpr (is_same_v<_Tp, __float128_t>) - return __u._M_f128; +#ifdef __BFLT16_DIG__ + else if constexpr (is_same_v<_Tp, __bflt16_t>) + return __u._M_bf16; +#endif +#ifdef __FLT16_DIG__ + else if constexpr (is_same_v<_Tp, _Float16>) + return __u._M_f16; +#endif +#ifdef __FLT32_DIG__ + else if constexpr (is_same_v<_Tp, _Float32>) + return __u._M_f32; +#endif +#ifdef __FLT64_DIG__ + else if constexpr (is_same_v<_Tp, _Float64>) + return __u._M_f64; #endif else if constexpr (derived_from<_Tp, _HandleBase>) return static_cast<_Tp&>(__u._M_handle); @@ -4120,36 +4178,25 @@ namespace __format else if constexpr (is_same_v<_Td, __ieee128>) return type_identity<__ieee128>(); #endif - -#if defined(__FLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) - else if constexpr (is_same_v<_Td, _Float16>) - return type_identity<float>(); +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 + else if constexpr (is_same_v<_Td, __float128>) + return type_identity<__float128>(); #endif - -#if defined(__BFLT16_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) - else if constexpr (is_same_v<_Td, decltype(0.0bf16)>) - return type_identity<float>(); +#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + else if constexpr (is_same_v<_Td, __format::__bflt16_t>) + return type_identity<__format::__bflt16_t>(); +#endif +#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + else if constexpr (is_same_v<_Td, _Float16>) + return type_identity<_Float16>(); #endif - #if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) else if constexpr (is_same_v<_Td, _Float32>) - return type_identity<float>(); + return type_identity<_Float32>(); #endif - #if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) else if constexpr (is_same_v<_Td, _Float64>) - return type_identity<double>(); -#endif - -#if _GLIBCXX_FORMAT_F128 -# if __FLT128_DIG__ - else if constexpr (is_same_v<_Td, _Float128>) - return type_identity<__format::__float128_t>(); -# endif -# if __SIZEOF_FLOAT128__ - else if constexpr (is_same_v<_Td, __float128>) - return type_identity<__format::__float128_t>(); -# endif + return type_identity<_Float64>(); #endif else if constexpr (__is_specialization_of<_Td, basic_string_view> || __is_specialization_of<_Td, basic_string>) @@ -4205,7 +4252,27 @@ namespace __format else if constexpr (is_same_v<_Tp, __ibm128>) return _Arg_ibm128; else if constexpr (is_same_v<_Tp, __ieee128>) - return _Arg_f128; + return _Arg_ieee128; +#endif +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 + else if constexpr (is_same_v<_Tp, __float128>) + return _Arg_float128; +#endif +#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + else if constexpr (is_same_v<_Tp, __format::__bflt16_t>) + return _Arg_bf16; +#endif +#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + else if constexpr (is_same_v<_Tp, _Float16>) + return _Arg_f16; +#endif +#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + else if constexpr (is_same_v<_Tp, _Float32>) + return _Arg_f32; +#endif +#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) + else if constexpr (is_same_v<_Tp, _Float64>) + return _Arg_f64; #endif else if constexpr (is_same_v<_Tp, const _CharT*>) return _Arg_str; @@ -4219,11 +4286,6 @@ namespace __format else if constexpr (is_same_v<_Tp, unsigned __int128>) return _Arg_u128; #endif - -#if _GLIBCXX_FORMAT_F128 == 2 - else if constexpr (is_same_v<_Tp, __format::__float128_t>) - return _Arg_f128; -#endif else if constexpr (is_same_v<_Tp, handle>) return _Arg_handle; } @@ -4296,13 +4358,33 @@ namespace __format #ifndef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT case _Arg_ldbl: return std::forward<_Visitor>(__vis)(_M_val._M_ldbl); +#if defined(__SIZEOF_FLOAT128__) && _GLIBCXX_FORMAT_F128 + case _Arg_float128: + return std::forward<_Visitor>(__vis)(_M_val._M_float128); +#endif #else - case _Arg_f128: - return std::forward<_Visitor>(__vis)(_M_val._M_f128); case _Arg_ibm128: return std::forward<_Visitor>(__vis)(_M_val._M_ibm128); + case _Arg_ieee128: + return std::forward<_Visitor>(__vis)(_M_val._M_ieee128); #endif +#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + case _Arg_bf16: + return std::forward<_Visitor>(__vis)(_M_val._M_bf16); +#endif +#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + case _Arg_f16: + return std::forward<_Visitor>(__vis)(_M_val._M_f16); +#endif +#if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + case _Arg_f32: + return std::forward<_Visitor>(__vis)(_M_val._M_f32); #endif +#if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64) + case _Arg_f64: + return std::forward<_Visitor>(__vis)(_M_val._M_f64); +#endif +#endif // __glibcxx_to_chars case _Arg_str: return std::forward<_Visitor>(__vis)(_M_val._M_str); case _Arg_sv: @@ -4320,14 +4402,7 @@ namespace __format case _Arg_u128: return std::forward<_Visitor>(__vis)(_M_val._M_u128); #endif - -#if _GLIBCXX_FORMAT_F128 == 2 - case _Arg_f128: - return std::forward<_Visitor>(__vis)(_M_val._M_f128); -#endif - default: - // _Arg_f16 etc. __builtin_unreachable(); } } diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index 1077e96..9a55b18 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -52,6 +52,20 @@ #if __cplusplus >= 201103L +#define __glibcxx_want_boyer_moore_searcher +#define __glibcxx_want_bind_front +#define __glibcxx_want_bind_back +#define __glibcxx_want_constexpr_functional +#define __glibcxx_want_copyable_function +#define __glibcxx_want_invoke +#define __glibcxx_want_invoke_r +#define __glibcxx_want_move_only_function +#define __glibcxx_want_not_fn +#define __glibcxx_want_ranges +#define __glibcxx_want_reference_wrapper +#define __glibcxx_want_transparent_operators +#include <bits/version.h> + #include <tuple> #include <type_traits> #include <bits/functional_hash.h> @@ -72,23 +86,10 @@ # include <bits/ranges_cmp.h> // std::identity, ranges::equal_to etc. # include <compare> #endif -#if __cplusplus > 202002L && _GLIBCXX_HOSTED -# include <bits/move_only_function.h> +#if defined(__glibcxx_move_only_function) || defined(__glibcxx_copyable_function) +# include <bits/funcwrap.h> #endif -#define __glibcxx_want_boyer_moore_searcher -#define __glibcxx_want_bind_front -#define __glibcxx_want_bind_back -#define __glibcxx_want_constexpr_functional -#define __glibcxx_want_invoke -#define __glibcxx_want_invoke_r -#define __glibcxx_want_move_only_function -#define __glibcxx_want_not_fn -#define __glibcxx_want_ranges -#define __glibcxx_want_reference_wrapper -#define __glibcxx_want_transparent_operators -#include <bits/version.h> - #endif // C++11 namespace std _GLIBCXX_VISIBILITY(default) diff --git a/libstdc++-v3/include/std/utility b/libstdc++-v3/include/std/utility index 1c15c75..8a85ccf 100644 --- a/libstdc++-v3/include/std/utility +++ b/libstdc++-v3/include/std/utility @@ -201,7 +201,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #ifdef __cpp_lib_to_underlying // C++ >= 23 /// Convert an object of enumeration type to its underlying type. template<typename _Tp> - [[nodiscard]] + [[nodiscard, __gnu__::__always_inline__]] constexpr underlying_type_t<_Tp> to_underlying(_Tp __value) noexcept { return static_cast<underlying_type_t<_Tp>>(__value); } diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in index d45ae63..417c8a1 100644 --- a/libstdc++-v3/src/c++23/std.cc.in +++ b/libstdc++-v3/src/c++23/std.cc.in @@ -1412,6 +1412,9 @@ export namespace std #if __cpp_lib_move_only_function using std::move_only_function; #endif +#if __cpp_lib_copyable_function + using std::copyable_function; +#endif using std::multiplies; using std::negate; using std::not_equal_to; diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/call.cc b/libstdc++-v3/testsuite/20_util/copyable_function/call.cc new file mode 100644 index 0000000..cf99757 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/copyable_function/call.cc @@ -0,0 +1,224 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <utility> +#include <testsuite_hooks.h> + +using std::copyable_function; + +using std::is_same_v; +using std::is_invocable_v; +using std::is_nothrow_invocable_v; +using std::invoke_result_t; + +// Check return types +static_assert( is_same_v<void, invoke_result_t<copyable_function<void()>>> ); +static_assert( is_same_v<int, invoke_result_t<copyable_function<int()>>> ); +static_assert( is_same_v<int&, invoke_result_t<copyable_function<int&()>>> ); + +// With const qualifier +static_assert( ! is_invocable_v< copyable_function<void()> const > ); +static_assert( ! is_invocable_v< copyable_function<void()> const &> ); +static_assert( is_invocable_v< copyable_function<void() const> > ); +static_assert( is_invocable_v< copyable_function<void() const> &> ); +static_assert( is_invocable_v< copyable_function<void() const> const > ); +static_assert( is_invocable_v< copyable_function<void() const> const &> ); + +// With no ref-qualifier +static_assert( is_invocable_v< copyable_function<void()> > ); +static_assert( is_invocable_v< copyable_function<void()> &> ); +static_assert( is_invocable_v< copyable_function<void() const> > ); +static_assert( is_invocable_v< copyable_function<void() const> &> ); +static_assert( is_invocable_v< copyable_function<void() const> const > ); +static_assert( is_invocable_v< copyable_function<void() const> const &> ); + +// With & ref-qualifier +static_assert( ! is_invocable_v< copyable_function<void()&> > ); +static_assert( is_invocable_v< copyable_function<void()&> &> ); +static_assert( is_invocable_v< copyable_function<void() const&> > ); +static_assert( is_invocable_v< copyable_function<void() const&> &> ); +static_assert( is_invocable_v< copyable_function<void() const&> const > ); +static_assert( is_invocable_v< copyable_function<void() const&> const &> ); + +// With && ref-qualifier +static_assert( is_invocable_v< copyable_function<void()&&> > ); +static_assert( ! is_invocable_v< copyable_function<void()&&> &> ); +static_assert( is_invocable_v< copyable_function<void() const&&> > ); +static_assert( ! is_invocable_v< copyable_function<void() const&&> &> ); +static_assert( is_invocable_v< copyable_function<void() const&&> const > ); +static_assert( ! is_invocable_v< copyable_function<void() const&&> const &> ); + +// With noexcept-specifier +static_assert( ! is_nothrow_invocable_v< copyable_function<void()> > ); +static_assert( ! is_nothrow_invocable_v< copyable_function<void() noexcept(false)> > ); +static_assert( is_nothrow_invocable_v< copyable_function<void() noexcept> > ); +static_assert( is_nothrow_invocable_v< copyable_function<void()& noexcept>& > ); + +void +test01() +{ + struct F + { + int operator()() { return 0; } + int operator()() const { return 1; } + }; + + copyable_function<int()> f0{F{}}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + copyable_function<int() const> f1{F{}}; + VERIFY( f1() == 1 ); + VERIFY( std::as_const(f1)() == 1 ); + VERIFY( std::move(f1)() == 1 ); + VERIFY( std::move(std::as_const(f1))() == 1 ); + + copyable_function<int()&> f2{F{}}; + VERIFY( f2() == 0 ); + // Not rvalue-callable: std::move(f2)() + + copyable_function<int() const&> f3{F{}}; + VERIFY( f3() == 1 ); + VERIFY( std::as_const(f3)() == 1 ); + VERIFY( std::move(f3)() == 1 ); + VERIFY( std::move(std::as_const(f3))() == 1 ); + + copyable_function<int()&&> f4{F{}}; + // Not lvalue-callable: f4() + VERIFY( std::move(f4)() == 0 ); + + copyable_function<int() const&&> f5{F{}}; + // Not lvalue-callable: f5() + VERIFY( std::move(f5)() == 1 ); + VERIFY( std::move(std::as_const(f5))() == 1 ); +} + +void +test02() +{ + struct F + { + int operator()() & { return 0; } + int operator()() && { return 1; } + }; + + copyable_function<int()> f0{F{}}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + copyable_function<int()&&> f1{F{}}; + // Not lvalue callable: f1() + VERIFY( std::move(f1)() == 1 ); + + copyable_function<int()&> f2{F{}}; + VERIFY( f2() == 0 ); + // Not rvalue-callable: std::move(f2)() +} + +void +test03() +{ + struct F + { + int operator()() const & { return 0; } + int operator()() && { return 1; } + }; + + copyable_function<int()> f0{F{}}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + copyable_function<int()&&> f1{F{}}; + // Not lvalue callable: f1() + VERIFY( std::move(f1)() == 1 ); + + copyable_function<int() const> f2{F{}}; + VERIFY( f2() == 0 ); + VERIFY( std::as_const(f2)() == 0 ); + VERIFY( std::move(f2)() == 0 ); + VERIFY( std::move(std::as_const(f2))() == 0 ); + + copyable_function<int() const &&> f3{F{}}; + // Not lvalue callable: f3() + VERIFY( std::move(f3)() == 0 ); + VERIFY( std::move(std::as_const(f3))() == 0 ); + + copyable_function<int() const &> f4{F{}}; + VERIFY( f4() == 0 ); + VERIFY( std::as_const(f4)() == 0 ); + // Not rvalue-callable: std::move(f4)() +} + +void +test04() +{ + struct F + { + int operator()() & { return 0; } + int operator()() && { return 1; } + int operator()() const & { return 2; } + int operator()() const && { return 3; } + }; + + copyable_function<int()> f0{F{}}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + copyable_function<int()&> f1{F{}}; + VERIFY( f1() == 0 ); + // Not rvalue-callable: std::move(f1)() + + copyable_function<int()&&> f2{F{}}; + // Not lvalue callable: f2() + VERIFY( std::move(f2)() == 1 ); + + copyable_function<int() const> f3{F{}}; + VERIFY( f3() == 2 ); + VERIFY( std::as_const(f3)() == 2 ); + VERIFY( std::move(f3)() == 2 ); + VERIFY( std::move(std::as_const(f3))() == 2 ); + + copyable_function<int() const &> f4{F{}}; + VERIFY( f4() == 2 ); + VERIFY( std::as_const(f4)() == 2 ); + // Not rvalue-callable: std::move(f4)() + + copyable_function<int() const &&> f5{F{}}; + // Not lvalue callable: f5() + VERIFY( std::move(f5)() == 3 ); + VERIFY( std::move(std::as_const(f5))() == 3 ); +} + +void +test05() +{ + int (*fp)() = [] { return 0; }; + copyable_function<int()> f0{fp}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + const copyable_function<int() const> f1{fp}; + VERIFY( f1() == 0 ); + VERIFY( std::move(f1)() == 0 ); +} + +struct Incomplete; + +void +test_params() +{ + std::copyable_function<void(Incomplete)> f1; + std::copyable_function<void(Incomplete&)> f2; + std::copyable_function<void(Incomplete&&)> f3; +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test_params(); +} diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/cons.cc b/libstdc++-v3/testsuite/20_util/copyable_function/cons.cc new file mode 100644 index 0000000..8d422dc --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/copyable_function/cons.cc @@ -0,0 +1,126 @@ +// { dg-do compile { target c++26 } } +// { dg-require-effective-target hosted } +// { dg-add-options no_pch } + +#include <functional> + +#ifndef __cpp_lib_copyable_function +# error "Feature-test macro for copyable_function missing in <functional>" +#elif __cpp_lib_copyable_function != 202306L +# error "Feature-test macro for copyable_function has wrong value in <functional>" +#endif + +using std::copyable_function; + +using std::is_constructible_v; +using std::is_copy_constructible_v; +using std::is_nothrow_default_constructible_v; +using std::is_nothrow_move_constructible_v; +using std::is_nothrow_constructible_v; +using std::nullptr_t; +using std::in_place_type_t; + +static_assert( is_nothrow_default_constructible_v<copyable_function<void()>> ); +static_assert( is_nothrow_constructible_v<copyable_function<void()>, nullptr_t> ); +static_assert( is_nothrow_move_constructible_v<copyable_function<void()>> ); +static_assert( is_copy_constructible_v<copyable_function<void()>> ); + +static_assert( is_constructible_v<copyable_function<void()>, void()> ); +static_assert( is_constructible_v<copyable_function<void()>, void(&)()> ); +static_assert( is_constructible_v<copyable_function<void()>, void(*)()> ); +static_assert( is_constructible_v<copyable_function<void()>, int()> ); +static_assert( is_constructible_v<copyable_function<void()>, int(&)()> ); +static_assert( is_constructible_v<copyable_function<void()>, int(*)()> ); +static_assert( ! is_constructible_v<copyable_function<void()>, void(int)> ); +static_assert( is_constructible_v<copyable_function<void(int)>, void(int)> ); + +static_assert( is_constructible_v<copyable_function<void(int)>, + in_place_type_t<void(*)(int)>, void(int)> ); + +static_assert( is_constructible_v<copyable_function<void()>, + void() noexcept> ); +static_assert( is_constructible_v<copyable_function<void() noexcept>, + void() noexcept> ); +static_assert( ! is_constructible_v<copyable_function<void() noexcept>, + void() > ); + +struct Q +{ + void operator()() const &; + void operator()() &&; +}; + +static_assert( is_constructible_v<copyable_function<void()>, Q> ); +static_assert( is_constructible_v<copyable_function<void() const>, Q> ); +static_assert( is_constructible_v<copyable_function<void() &>, Q> ); +static_assert( is_constructible_v<copyable_function<void() const &>, Q> ); +static_assert( is_constructible_v<copyable_function<void() &&>, Q> ); +static_assert( is_constructible_v<copyable_function<void() const &&>, Q> ); + +struct R +{ + void operator()() &; + void operator()() &&; +}; + +static_assert( is_constructible_v<copyable_function<void()>, R> ); +static_assert( is_constructible_v<copyable_function<void()&>, R> ); +static_assert( is_constructible_v<copyable_function<void()&&>, R> ); +static_assert( ! is_constructible_v<copyable_function<void() const>, R> ); +static_assert( ! is_constructible_v<copyable_function<void() const&>, R> ); +static_assert( ! is_constructible_v<copyable_function<void() const&&>, R> ); + +// The following nothrow-constructible guarantees are a GCC extension, +// not required by the standard. + +static_assert( is_nothrow_constructible_v<copyable_function<void()>, void()> ); +static_assert( is_nothrow_constructible_v<copyable_function<void(int)>, + in_place_type_t<void(*)(int)>, + void(int)> ); + +// These types are all small and nothrow move constructible +struct F { void operator()(); }; +struct G { void operator()() const; }; +static_assert( is_nothrow_constructible_v<copyable_function<void()>, F> ); +static_assert( is_nothrow_constructible_v<copyable_function<void()>, G> ); +static_assert( is_nothrow_constructible_v<copyable_function<void() const>, G> ); + +struct H { + H(int); + H(int, int) noexcept; + void operator()() noexcept; +}; +static_assert( is_nothrow_constructible_v<copyable_function<void()>, H> ); +static_assert( is_nothrow_constructible_v<copyable_function<void() noexcept>, + H> ); +static_assert( ! is_nothrow_constructible_v<copyable_function<void() noexcept>, + in_place_type_t<H>, int> ); +static_assert( is_nothrow_constructible_v<copyable_function<void() noexcept>, + in_place_type_t<H>, int, int> ); + +struct I { + I(int, const char*); + I(std::initializer_list<char>); + int operator()() const noexcept; +}; + +static_assert( is_constructible_v<copyable_function<void()>, + std::in_place_type_t<I>, + int, const char*> ); +static_assert( is_constructible_v<copyable_function<void()>, + std::in_place_type_t<I>, + std::initializer_list<char>> ); + +void +test_instantiation() +{ + // Instantiate the constructor bodies + copyable_function<void()> f0; + copyable_function<void()> f1(nullptr); + copyable_function<void()> f2( I(1, "two") ); + copyable_function<void()> f3(std::in_place_type<I>, 3, "four"); + copyable_function<void()> f4(std::in_place_type<I>, // PR libstdc++/102825 + { 'P', 'R', '1', '0', '2', '8', '2', '5'}); + auto f5 = std::move(f4); + f4 = std::move(f5); +} diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc b/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc new file mode 100644 index 0000000..e678e16 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc @@ -0,0 +1,251 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <testsuite_hooks.h> + +using std::copyable_function; + +static_assert( !std::is_constructible_v<std::copyable_function<void()>, + std::copyable_function<void()&>> ); +static_assert( !std::is_constructible_v<std::copyable_function<void()>, + std::copyable_function<void()&&>> ); +static_assert( !std::is_constructible_v<std::copyable_function<void()&>, + std::copyable_function<void()&&>> ); +static_assert( !std::is_constructible_v<std::copyable_function<void() const>, + std::copyable_function<void()>> ); + +// Non-trivial args, guarantess that type is not passed by copy +struct CountedArg +{ + CountedArg() = default; + CountedArg(const CountedArg& f) noexcept : counter(f.counter) { ++counter; } + CountedArg& operator=(CountedArg&&) = delete; + + int counter = 0; +}; +CountedArg const c; + +// When copyable_function or move_only_function is constructed from other copyable_function, +// the compiler can avoid double indirection per C++26 [func.wrap.general] p2. + +void +test01() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::copyable_function<int(CountedArg) const noexcept> c1(f); + using CF = std::copyable_function<int(CountedArg) const noexcept>; + VERIFY( c1(c) == 1 ); + + std::copyable_function<int(CountedArg) const> c2a(c1); + VERIFY( c2a(c) == 1 ); + + std::copyable_function<int(CountedArg) const> c2b(static_cast<CF>(c1)); + VERIFY( c2b(c) == 1 ); + + std::move_only_function<int(CountedArg) const> m2a(c1); + VERIFY( m2a(c) == 1 ); + + std::move_only_function<int(CountedArg) const> m2b(static_cast<CF>(c1)); + VERIFY( m2b(c) == 1 ); + + std::copyable_function<int(CountedArg)> c3a(c1); + VERIFY( c3a(c) == 1 ); + + std::copyable_function<int(CountedArg)> c3b(static_cast<CF>(c1)); + VERIFY( c3b(c) == 1 ); + + std::move_only_function<int(CountedArg)> m3a(c1); + VERIFY( m3a(c) == 1 ); + + std::move_only_function<int(CountedArg)> m3b(static_cast<CF>(c1)); + VERIFY( m3b(c) == 1 ); + + // Invokers internally uses Counted&& for non-trivial types, + // sinature remain compatible. + std::copyable_function<int(CountedArg&&)> c4a(c1); + VERIFY( c4a({}) == 0 ); + + std::copyable_function<int(CountedArg&&)> c4b(static_cast<CF>(c1)); + VERIFY( c4b({}) == 0 ); + + std::move_only_function<int(CountedArg&&)> m4a(c1); + VERIFY( m4a({}) == 0 ); + + std::move_only_function<int(CountedArg&&)> m4b(static_cast<CF>(c1)); + VERIFY( m4b({}) == 0 ); + + std::copyable_function<int(CountedArg&&)&> c5a(c1); + VERIFY( c5a({}) == 0 ); + + std::copyable_function<int(CountedArg&&)&&> c5b(static_cast<CF>(c1)); + VERIFY( std::move(c5b)({}) == 0 ); + + std::move_only_function<int(CountedArg&&)&> m5a(c1); + VERIFY( m5a({}) == 0 ); + + std::move_only_function<int(CountedArg&&)&&> m5b(static_cast<CF>(c1)); + VERIFY( std::move(m5b)({}) == 0 ); + + // Incompatible signatures + std::copyable_function<long(CountedArg) const noexcept> c6a(c1); + VERIFY( c6a(c) == 2 ); + + std::copyable_function<long(CountedArg) const noexcept> c6b(static_cast<CF>(c1)); + VERIFY( c6b(c) == 2 ); + + std::move_only_function<long(CountedArg) const noexcept> m6a(c1); + VERIFY( m6a(c) == 2 ); + + std::move_only_function<long(CountedArg) const noexcept> m6b(static_cast<CF>(c1)); + VERIFY( m6b(c) == 2 ); +} + +void +test02() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::copyable_function<int(CountedArg) const noexcept> c1(f); + using CF = std::copyable_function<int(CountedArg) const noexcept>; + VERIFY( c1(c) == 1 ); + + std::copyable_function<int(CountedArg) const> c2; + c2 = c1; + VERIFY( c2(c) == 1 ); + c2 = static_cast<CF>(c1); + VERIFY( c2(c) == 1 ); + + std::move_only_function<int(CountedArg) const> m2; + m2 = c1; + VERIFY( m2(c) == 1 ); + m2 = static_cast<CF>(c1); + VERIFY( m2(c) == 1 ); + + // Incompatible signatures + std::copyable_function<long(CountedArg) const noexcept> c3; + c3 = c1; + VERIFY( c3(c) == 2 ); + c3 = static_cast<CF>(c1); + VERIFY( c3(c) == 2 ); + + std::move_only_function<long(CountedArg) const noexcept> m3; + m3 = c1; + VERIFY( m3(c) == 2 ); + m3 = static_cast<CF>(c1); + VERIFY( m3(c) == 2 ); +} + +void +test03() +{ + std::copyable_function<int(long) const noexcept> c1; + VERIFY( c1 == nullptr ); + + std::copyable_function<int(long) const> c2(c1); + VERIFY( c2 == nullptr ); + c2 = c1; + VERIFY( c2 == nullptr ); + c2 = std::move(c1); + VERIFY( c2 == nullptr ); + + std::copyable_function<bool(int) const> c3(std::move(c1)); + VERIFY( c3 == nullptr ); + c3 = c1; + VERIFY( c3 == nullptr ); + c3 = std::move(c1); + VERIFY( c3 == nullptr ); + + // LWG4255 move_only_function constructor should recognize empty + // copyable_functions + std::move_only_function<int(long) const noexcept> m1(c1); + VERIFY( m1 == nullptr ); + m1 = c1; + VERIFY( m1 == nullptr ); + m1 = std::move(c1); + VERIFY( m1 == nullptr ); + + std::move_only_function<int(long) const> m2(c1); + VERIFY( m2 == nullptr ); + m2 = c1; + VERIFY( m2 == nullptr ); + m2 = std::move(c1); + VERIFY( m2 == nullptr ); + + std::move_only_function<bool(int) const> m3(std::move(c1)); + VERIFY( m3 == nullptr ); + m3 = c1; + VERIFY( m3 == nullptr ); + m3 = std::move(c1); + VERIFY( m3 == nullptr ); +} + +void +test04() +{ + struct F + { + int operator()(CountedArg const& arg) noexcept + { return arg.counter; } + + int operator()(CountedArg const& arg) const noexcept + { return arg.counter + 1000; } + }; + + F f; + std::copyable_function<int(CountedArg) const> c1(f); + VERIFY( c1(c) == 1001 ); + + // Call const overload as std::copyable_function<int(CountedArg) const> + // inside td::copyable_function<int(CountedArg)> would do. + std::copyable_function<int(CountedArg)> c2(c1); + VERIFY( c2(c) == 1001 ); + std::move_only_function<int(CountedArg)> m2(c1); + VERIFY( m2(c) == 1001 ); + + std::copyable_function<int(CountedArg)> m3(f); + VERIFY( m3(c) == 1 ); +} + +void +test05() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::copyable_function<int(CountedArg)> w1(f); + // copyable_function stores copyable_function due incompatibile signatures + std::copyable_function<int(CountedArg const&)> w2(std::move(w1)); + // copy is made when passing to int(CountedArg) + VERIFY( w2(c) == 1 ); + // wrapped 3 times + w1 = std::move(w2); + VERIFY( w1(c) == 2 ); + // wrapped 4 times + w2 = std::move(w1); + VERIFY( w2(c) == 2 ); + // wrapped 5 times + w1 = std::move(w2); + VERIFY( w1(c) == 3 ); +} + +void +test06() +{ + // No special interoperability with std::function + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function<int(CountedArg)> f1(f); + std::copyable_function<int(CountedArg) const> c1(std::move(f1)); + VERIFY( c1(c) == 2 ); + + std::copyable_function<int(CountedArg) const> c2(f); + std::function<int(CountedArg)> f2(c2); + VERIFY( f2(c) == 2 ); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); +} diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/copy.cc b/libstdc++-v3/testsuite/20_util/copyable_function/copy.cc new file mode 100644 index 0000000..6445a27 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/copyable_function/copy.cc @@ -0,0 +1,154 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <testsuite_hooks.h> + +using std::copyable_function; + +void +test01() +{ + // Small type with non-throwing move constructor. Not allocated on the heap. + struct F + { + F() = default; + F(const F& f) : counters(f.counters) { ++counters.copy; } + F(F&& f) noexcept : counters(f.counters) { ++counters.move; } + + F& operator=(F&&) = delete; + + struct Counters + { + int copy = 0; + int move = 0; + } counters; + + const Counters& operator()() const { return counters; } + }; + + F f; + std::copyable_function<const F::Counters&() const> m1(f); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + // This will copy construct a new target object + auto m2 = m1; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m2().copy == 2 ); + VERIFY( m2().move == 0 ); + + m1 = m2; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m1().copy == 3 ); + VERIFY( m1().move == 1 ); // Copies object first and then swaps + + m1 = m1; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m1().copy == 4 ); + VERIFY( m1().move == 2 ); // Copies object first and then swaps + + m2 = f; + VERIFY( m2().copy == 1 ); + VERIFY( m2().move == 1 ); // Copy construct target object, then swap into m2. +} + +void +test02() +{ + // Move constructor is potentially throwing. Allocated on the heap. + struct F + { + F() = default; + F(const F& f) noexcept : counters(f.counters) { ++counters.copy; } + F(F&& f) noexcept(false) : counters(f.counters) { ++counters.move; } + + F& operator=(F&&) = delete; + + struct Counters + { + int copy = 0; + int move = 0; + } counters; + + Counters operator()() const noexcept { return counters; } + }; + + F f; + std::copyable_function<F::Counters() const> m1(f); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + // The target object is on the heap, but we need to allocate new one + auto m2 = m1; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m2().copy == 2 ); + VERIFY( m2().move == 0 ); + + m1 = m2; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m1().copy == 3 ); + VERIFY( m1().move == 0 ); + + m1 = m1; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m1().copy == 4 ); + VERIFY( m1().move == 0 ); + + m2 = f; + VERIFY( m2().copy == 1 ); + VERIFY( m2().move == 0 ); +} + +void +test03() +{ + // Small type with non-throwing, but not non-trivial move constructor. + struct F + { + F(int i) noexcept : id(i) {} + F(const F& f) : id(f.id) + { if (id == 3) throw id; } + F(F&& f) noexcept : id(f.id) { } + + int operator()() const + { return id; } + + int id; + }; + + std::copyable_function<int() const> m1(std::in_place_type<F>, 1); + const std::copyable_function<int() const> m2(std::in_place_type<F>, 2); + const std::copyable_function<int() const> m3(std::in_place_type<F>, 3); + + try + { + auto mc = m3; + VERIFY( false ); + } + catch(int i) + { + VERIFY( i == 3 ); + } + + m1 = m2; + VERIFY( m1() == 2 ); + + try + { + m1 = m3; + VERIFY( false ); + } + catch (int i) + { + VERIFY( i == 3 ); + } + VERIFY( m1() == 2 ); +} + +int main() +{ + test01(); + test02(); + test03(); +} diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/move.cc b/libstdc++-v3/testsuite/20_util/copyable_function/move.cc new file mode 100644 index 0000000..ec9d0d1 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/copyable_function/move.cc @@ -0,0 +1,120 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <testsuite_hooks.h> + +using std::copyable_function; + +void +test01() +{ + // Small type with non-throwing move constructor. Not allocated on the heap. + struct F + { + F() = default; + F(const F& f) : counters(f.counters) { ++counters.copy; } + F(F&& f) noexcept : counters(f.counters) { ++counters.move; } + + F& operator=(F&&) = delete; + + struct Counters + { + int copy = 0; + int move = 0; + } counters; + + const Counters& operator()() const { return counters; } + }; + + F f; + std::copyable_function<const F::Counters&() const> m1(f); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + // Standard specifies move assigment as copy and swap + m1 = std::move(m1); + VERIFY( m1 != nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + // This will move construct a new target object and destroy the old one: + auto m2 = std::move(m1); + VERIFY( m1 == nullptr && m2 != nullptr ); + VERIFY( m2().copy == 1 ); + VERIFY( m2().move == 1 ); + + m1 = std::move(m2); + VERIFY( m1 != nullptr && m2 == nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 2 ); + + m2 = std::move(f); + VERIFY( m2().copy == 0 ); + VERIFY( m2().move == 2 ); // Move construct target object, then swap into m2. + const int moves = m1().move + m2().move; + // This will do three moves: + swap(m1, m2); + VERIFY( m1().copy == 0 ); + VERIFY( m2().copy == 1 ); + VERIFY( (m1().move + m2().move) == (moves + 3) ); +} + +void +test02() +{ + // Move constructor is potentially throwing. Allocated on the heap. + struct F + { + F() = default; + F(const F& f) noexcept : counters(f.counters) { ++counters.copy; } + F(F&& f) noexcept(false) : counters(f.counters) { ++counters.move; } + + F& operator=(F&&) = delete; + + struct Counters + { + int copy = 0; + int move = 0; + } counters; + + Counters operator()() const noexcept { return counters; } + }; + + F f; + std::copyable_function<F::Counters() const> m1(f); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + m1 = std::move(m1); + VERIFY( m1 != nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + // The target object is on the heap so this just moves a pointer: + auto m2 = std::move(m1); + VERIFY( m1 == nullptr && m2 != nullptr ); + VERIFY( m2().copy == 1 ); + VERIFY( m2().move == 0 ); + + m1 = std::move(m2); + VERIFY( m1 != nullptr && m2 == nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + m2 = std::move(f); + VERIFY( m2().copy == 0 ); + VERIFY( m2().move == 1 ); + const int moves = m1().move + m2().move; + // This just swaps the pointers, so no moves: + swap(m1, m2); + VERIFY( m1().copy == 0 ); + VERIFY( m2().copy == 1 ); + VERIFY( (m1().move + m2().move) == moves ); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/20_util/move_only_function/call.cc b/libstdc++-v3/testsuite/20_util/move_only_function/call.cc index bfc609a..217de37 100644 --- a/libstdc++-v3/testsuite/20_util/move_only_function/call.cc +++ b/libstdc++-v3/testsuite/20_util/move_only_function/call.cc @@ -190,6 +190,19 @@ test04() VERIFY( std::move(std::as_const(f5))() == 3 ); } +void +test05() +{ + int (*fp)() = [] { return 0; }; + move_only_function<int()> f0{fp}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + const move_only_function<int() const> f1{fp}; + VERIFY( f1() == 0 ); + VERIFY( std::move(f1)() == 0 ); +} + struct Incomplete; void @@ -206,5 +219,6 @@ int main() test02(); test03(); test04(); + test05(); test_params(); } diff --git a/libstdc++-v3/testsuite/20_util/move_only_function/conv.cc b/libstdc++-v3/testsuite/20_util/move_only_function/conv.cc new file mode 100644 index 0000000..3da5e9e --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/move_only_function/conv.cc @@ -0,0 +1,188 @@ +// { dg-do run { target c++23 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <testsuite_hooks.h> + +using std::move_only_function; + +static_assert( !std::is_constructible_v<std::move_only_function<void()>, + std::move_only_function<void()&>> ); +static_assert( !std::is_constructible_v<std::move_only_function<void()>, + std::move_only_function<void()&&>> ); +static_assert( !std::is_constructible_v<std::move_only_function<void()&>, + std::move_only_function<void()&&>> ); +static_assert( !std::is_constructible_v<std::move_only_function<void() const>, + std::move_only_function<void()>> ); + +// Non-trivial args, guarantess that type is not passed by copy +struct CountedArg +{ + CountedArg() = default; + CountedArg(const CountedArg& f) noexcept : counter(f.counter) { ++counter; } + CountedArg& operator=(CountedArg&&) = delete; + + int counter = 0; +}; +CountedArg const c; + +// When move_only_functions is constructed from other move_only_function, +// the compiler can avoid double indirection per C++26 [func.wrap.general] p2. + +void +test01() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::move_only_function<int(CountedArg) const noexcept> m1(f); + VERIFY( m1(c) == 1 ); + + std::move_only_function<int(CountedArg) const> m2(std::move(m1)); + VERIFY( m2(c) == 1 ); + + std::move_only_function<int(CountedArg)> m3(std::move(m2)); + VERIFY( m3(c) == 1 ); + + // Invokers internally uses Counted&& for non-trivial types, + // sinature remain compatible. + std::move_only_function<int(CountedArg&&)> m4(std::move(m3)); + VERIFY( m4({}) == 0 ); + + std::move_only_function<int(CountedArg&&)&&> m5(std::move(m4)); + VERIFY( std::move(m5)({}) == 0 ); + + m4 = f; + std::move_only_function<int(CountedArg&&)&> m7(std::move(m4)); + VERIFY( m7({}) == 0 ); + + m4 = f; + std::move_only_function<int(CountedArg&&)&> m8(std::move(m4)); + VERIFY( m8({}) == 0 ); + + // Incompatible signatures + m1 = f; + std::move_only_function<long(CountedArg) const noexcept> m9(std::move(m1)); + VERIFY( m9(c) == 2 ); +} + +void +test02() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::move_only_function<int(CountedArg) const noexcept> m1(f); + VERIFY( m1(c) == 1 ); + + std::move_only_function<int(CountedArg) const> m2; + m2 = std::move(m1); + VERIFY( m2(c) == 1 ); + + std::move_only_function<int(CountedArg)> m3; + m3 = std::move(m2); + VERIFY( m3(c) == 1 ); + + // Invokers internally uses Counted&& for non-trivial types, + // sinature remain compatible. + std::move_only_function<int(CountedArg&&)> m4; + m4 = std::move(m3); + VERIFY( m4({}) == 0 ); + + std::move_only_function<int(CountedArg&&)&&> m5; + m5 = std::move(m4); + VERIFY( std::move(m5)({}) == 0 ); + + m4 = f; + std::move_only_function<int(CountedArg&&)&> m7; + m7 = std::move(m4); + VERIFY( m7({}) == 0 ); + + m4 = f; + std::move_only_function<int(CountedArg&&)&> m8; + m8 = std::move(m4); + VERIFY( m8({}) == 0 ); + + m1 = f; + std::move_only_function<long(CountedArg) const noexcept> m9; + m9 = std::move(m1); + VERIFY( m9(c) == 2 ); +} + +void +test03() +{ + std::move_only_function<int(long) const noexcept> e; + VERIFY( e == nullptr ); + + std::move_only_function<int(long) const> e2(std::move(e)); + VERIFY( e2 == nullptr ); + e2 = std::move(e); + VERIFY( e2 == nullptr ); + + std::move_only_function<bool(int) const> e3(std::move(e)); + VERIFY( e3 == nullptr ); + e3 = std::move(e); + VERIFY( e3 == nullptr ); +} + +void +test04() +{ + struct F + { + int operator()(CountedArg const& arg) noexcept + { return arg.counter; } + + int operator()(CountedArg const& arg) const noexcept + { return arg.counter + 1000; } + }; + + F f; + std::move_only_function<int(CountedArg) const> m1(f); + VERIFY( m1(c) == 1001 ); + + // Call const overload as std::move_only_function<int(CountedArg) const> + // inside std::move_only_function<int(CountedArg)> would do. + std::move_only_function<int(CountedArg)> m2(std::move(m1)); + VERIFY( m2(c) == 1001 ); + + std::move_only_function<int(CountedArg)> m3(f); + VERIFY( m3(c) == 1 ); +} + +void +test05() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::move_only_function<int(CountedArg)> w1(f); + // move_only_function stores move_only_function due incompatibile signatures + std::move_only_function<int(CountedArg const&)> w2(std::move(w1)); + // copy is made when passing to int(CountedArg) + VERIFY( w2(c) == 1 ); + // wrapped 3 times + w1 = std::move(w2); + VERIFY( w1(c) == 2 ); + // wrapped 4 times + w2 = std::move(w1); + VERIFY( w2(c) == 2 ); + // wrapped 5 times + w1 = std::move(w2); + VERIFY( w1(c) == 3 ); +} + +void +test06() +{ + // No special interoperability with std::function + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function<int(CountedArg)> f1(f); + std::move_only_function<int(CountedArg) const> m1(std::move(f1)); + VERIFY( m1(c) == 2 ); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); +} diff --git a/libstdc++-v3/testsuite/20_util/move_only_function/move.cc b/libstdc++-v3/testsuite/20_util/move_only_function/move.cc index 51e31a6..6da02c9 100644 --- a/libstdc++-v3/testsuite/20_util/move_only_function/move.cc +++ b/libstdc++-v3/testsuite/20_util/move_only_function/move.cc @@ -32,6 +32,12 @@ test01() VERIFY( m1().copy == 1 ); VERIFY( m1().move == 0 ); + // Standard specifies move assigment as copy and swap + m1 = std::move(m1); + VERIFY( m1 != nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + // This will move construct a new target object and destroy the old one: auto m2 = std::move(m1); VERIFY( m1 == nullptr && m2 != nullptr ); @@ -80,6 +86,11 @@ test02() VERIFY( m1().copy == 1 ); VERIFY( m1().move == 0 ); + m1 = std::move(m1); + VERIFY( m1 != nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + // The target object is on the heap so this just moves a pointer: auto m2 = std::move(m1); VERIFY( m1 == nullptr && m2 != nullptr ); diff --git a/libstdc++-v3/testsuite/std/format/arguments/args.cc b/libstdc++-v3/testsuite/std/format/arguments/args.cc index 4c50bc7..6029675 100644 --- a/libstdc++-v3/testsuite/std/format/arguments/args.cc +++ b/libstdc++-v3/testsuite/std/format/arguments/args.cc @@ -164,24 +164,6 @@ void test_visited_as_handle() #endif } -template<typename E, typename S> -void test_visited_as() -{ - auto v = static_cast<S>(1.0); - auto store = std::make_format_args(v); - std::format_args args = store; - - auto is_expected_val = [v](auto arg) { - if constexpr (std::is_same_v<decltype(arg), E>) - return arg == static_cast<E>(v); - return false; - }; - VERIFY( std::visit_format_arg(is_expected_val, args.get(0)) ); -#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit - VERIFY( args.get(0).visit(is_expected_val) ); -#endif -} - template<typename T> concept can_format = std::is_default_constructible_v<std::formatter<T, char>>; @@ -195,30 +177,31 @@ int main() test_visited_as_handle<__int128>(); test_visited_as_handle<unsigned __int128>(); #endif -// TODO: This should be visited as handle. -#ifdef __STDCPP_FLOAT16_T__ - if constexpr (can_format<_Float16>) - test_visited_as<float, _Float16>(); -#endif -#ifdef __STDCPP_BFLOAT16_T__ +#ifdef __BFLT16_DIG__ if constexpr (can_format<__gnu_cxx::__bfloat16_t>) - test_visited_as<float, __gnu_cxx::__bfloat16_t>(); + test_visited_as_handle<__gnu_cxx::__bfloat16_t>(); +#endif +#ifdef __FLT16_DIG__ + if constexpr (can_format<_Float16>) + test_visited_as_handle<_Float16>(); #endif #ifdef __FLT32_DIG__ if constexpr (can_format<_Float32>) - test_visited_as<float, _Float32>(); + test_visited_as_handle<_Float32>(); #endif #ifdef __FLT64_DIG__ if constexpr (can_format<_Float64>) - test_visited_as<double, _Float64>(); + test_visited_as_handle<_Float64>(); #endif #ifdef __FLT128_DIG__ if constexpr (can_format<_Float128>) -# ifdef _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128 - test_visited_as<long double, _Float128>(); -# else test_visited_as_handle<_Float128>(); -# endif +#endif +#ifdef __SIZEOF_FLOAT128__ + // __ieee128 is same type as __float128, and may be long double + if constexpr (!std::is_same_v<__float128, long double>) + if constexpr (can_format<__float128>) + test_visited_as_handle<__float128>(); #endif #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT if constexpr (!std::is_same_v<__ieee128, long double>) diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx.cc b/libstdc++-v3/testsuite/std/format/parse_ctx.cc index b5dd7cd..adafc58 100644 --- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc +++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc @@ -443,6 +443,8 @@ test_custom() } #if __cpp_lib_format >= 202305 +#include <stdfloat> + struct X { }; template<> @@ -458,13 +460,20 @@ struct std::formatter<X, char> if (spec == "int") { pc.check_dynamic_spec_integral(pc.next_arg_id()); - integer = true; + type = Type::integral; } else if (spec == "str") { pc.check_dynamic_spec_string(pc.next_arg_id()); - integer = false; + type = Type::string; + } + else if (spec == "float") + { + pc.check_dynamic_spec<float, double, long double>(pc.next_arg_id()); + type = Type::floating; } + else if (spec == "other") + type = Type::other; else throw std::format_error("invalid format-spec"); return pc.begin() + spec.size(); @@ -474,13 +483,44 @@ struct std::formatter<X, char> format(X, std::format_context& c) const { std::visit_format_arg([this]<typename T>(T) { // { dg-warning "deprecated" "" { target c++26 } } - if (is_integral_v<T> != this->integer) - throw std::format_error("invalid argument type"); + constexpr bool is_handle + = std::is_same_v<std::basic_format_arg<std::format_context>::handle, T>; + constexpr bool is_integral + = std::is_same_v<int, T> || std::is_same_v<unsigned int, T> + || is_same_v<long long, T> || std::is_same_v<unsigned long long, T>; + constexpr bool is_string + = std::is_same_v<const char*, T> || std::is_same_v<std::string_view, T>; + constexpr bool is_floating + = std::is_same_v<float, T> || std::is_same_v<double, T> + || std::is_same_v<long double, T>; + switch (this->type) + { + case Type::other: + if (is_handle) return; + break; + case Type::integral: + if (is_integral) return; + break; + case Type::string: + if (is_string) return; + break; + case Type::floating: + if (is_floating) return; + break; + } + throw std::format_error("invalid argument type"); }, c.arg(1)); return c.out(); } private: - bool integer = false; + enum class Type + { + other, + integral, + string, + floating, + }; + Type type = Type::other; }; #endif @@ -497,6 +537,28 @@ test_dynamic_type_check() (void) std::format("{:int}", X{}, 42L); (void) std::format("{:str}", X{}, "H2G2"); + (void) std::format("{:float}", X{}, 10.0); + +#ifdef __STDCPP_FLOAT16_T__ + if constexpr (std::formattable<std::bfloat16_t, char>) + (void) std::format("{:other}", X{}, 10.0bf16); +#endif +#ifdef __STDCPP_FLOAT16_T__ + if constexpr (std::formattable<std::float16_t, char>) + (void) std::format("{:other}", X{}, 10.0f16); +#endif +#ifdef __STDCPP_FLOAT32_T__ + if constexpr (std::formattable<std::float32_t, char>) + (void) std::format("{:other}", X{}, 10.0f32); +#endif +#ifdef __STDCPP_FLOAT64_T__ + if constexpr (std::formattable<std::float64_t, char>) + (void) std::format("{:other}", X{}, 10.0f64); +#endif +#ifdef __STDCPP_FLOAT128_T__ + if constexpr (std::formattable<std::float128_t, char>) + (void) std::format("{:other}", X{}, 10.0f128); +#endif #endif } |