This patch implements C++26 function_ref as specified in P0792R14, with correction for constraints for constructor accepting nontype_t parameter from LWG 4256.
As function_ref may store a pointer to the const object, __Ptrs::_M_obj is changed to const void*, so again we do not cast away const from const objects. To help with necessary cast, a __polyfunc::__cast_to helper is added, that accepts a reference to that type. The _Invoker now defines additional call methods used by function_ref: _S_ptrs() for invoking target passed by reference, and __S_nttp, _S_bind_ptr, _S_bind_ref for handling constructors accepting nontype_t. The existing _S_call_storage is changed to thin wrappers, that initialies _Ptrs, and forwards to _S_call_ptrs. This reduced the most uses of _Storage::_M_ptr and _Storage::_M_ref, so this functions was removed, and _Manager uses were adjusted. Finally we make function_ref available in freestanding mode, as move_only_function and copyable_function are currently only available in hosted, so we define _Manager and _Mo_base only if either __glibcxx_move_only_function or __glibcxx_copyable_function is defined. PR libstdc++/119126 libstdc++-v3/ChangeLog: * doc/doxygen/stdheader.cc: Added funcref_impl.h file. * include/Makefile.am: Added funcref_impl.h file. * include/Makefile.in: Added funcref_impl.h file. * include/bits/funcref_impl.h: New file. * include/bits/funcwrap.h: (_Ptrs::_M_obj): Const-qualify. (_Storage::_M_ptr, _Storage::_M_ref): Remove. (__polyfunc::__cast_to) Define. (_Base_invoker::_S_ptrs, _Base_invoker::_S_nttp) (_Base_invoker::_S_bind_ptrs, _Base_invoker::_S_bind_ref) (_Base_invoker::_S_call_ptrs): Define. (_Base_invoker::_S_call_storage): Foward to _S_call_ptrs. (_Manager::_S_local, _Manager::_S_ptr): Adjust for _M_obj being const qualified. (__polyfunc::_Manager, __polyfunc::_Mo_base): Guard with __glibcxx_move_only_function || __glibcxx_copyable_function. (__polyfunc::__skip_first_arg, __polyfunc::__deduce_funcref) (std::function_ref) [__glibcxx_function_ref]: Define. * include/bits/utility.h (std::nontype_t, std::nontype) (__is_nontype_v) [__glibcxx_function_ref]: Define. * include/bits/version.def: Define function_ref. * include/bits/version.h: Regenerate. * include/std/functional: Define __cpp_lib_function_ref. * src/c++23/std.cc.in (std::nontype_t, std::nontype) (std::function_ref) [__cpp_lib_function_ref]: Export. * testsuite/20_util/function_ref/assign.cc: New test. * testsuite/20_util/function_ref/call.cc: New test. * testsuite/20_util/function_ref/cons.cc: New test. * testsuite/20_util/function_ref/cons_neg.cc: New test. * testsuite/20_util/function_ref/conv.cc: New test. * testsuite/20_util/function_ref/deduction.cc: New test. --- This updated patch addresses review comments, and add a deduction guide for the function_ref that was previously missing. The handling of _GLIBCXX_MOF_REF and related macros was removed from the funcref_impl.h, as it is not used currently. For the ifdef checks, I replaced defined(__cpp_lib_XXXX) with __cpp_lib_XXXX consistently. The unused __is_function_ref variable template was removed. All tests expect conv.cc now test with freestanding. Tested on x86_64-linux. Tested *function_ref* with -ffreestanding. libstdc++-v3/doc/doxygen/stdheader.cc | 1 + libstdc++-v3/include/Makefile.am | 1 + libstdc++-v3/include/Makefile.in | 1 + libstdc++-v3/include/bits/funcref_impl.h | 188 +++++++++++++++ libstdc++-v3/include/bits/funcwrap.h | 183 +++++++++++---- libstdc++-v3/include/bits/utility.h | 17 ++ libstdc++-v3/include/bits/version.def | 8 + libstdc++-v3/include/bits/version.h | 10 + libstdc++-v3/include/std/functional | 3 +- libstdc++-v3/src/c++23/std.cc.in | 7 + .../testsuite/20_util/function_ref/assign.cc | 108 +++++++++ .../testsuite/20_util/function_ref/call.cc | 144 ++++++++++++ .../testsuite/20_util/function_ref/cons.cc | 218 ++++++++++++++++++ .../20_util/function_ref/cons_neg.cc | 30 +++ .../testsuite/20_util/function_ref/conv.cc | 152 ++++++++++++ .../20_util/function_ref/deduction.cc | 103 +++++++++ 16 files changed, 1127 insertions(+), 47 deletions(-) create mode 100644 libstdc++-v3/include/bits/funcref_impl.h create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/assign.cc create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/call.cc create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/cons.cc create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/conv.cc create mode 100644 libstdc++-v3/testsuite/20_util/function_ref/deduction.cc diff --git a/libstdc++-v3/doc/doxygen/stdheader.cc b/libstdc++-v3/doc/doxygen/stdheader.cc index 839bfc81bc0..938b2b04a26 100644 --- a/libstdc++-v3/doc/doxygen/stdheader.cc +++ b/libstdc++-v3/doc/doxygen/stdheader.cc @@ -55,6 +55,7 @@ void init_map() headers["functional_hash.h"] = "functional"; headers["mofunc_impl.h"] = "functional"; headers["cpyfunc_impl.h"] = "functional"; + headers["funcref_impl.h"] = "functional"; headers["funcwrap.h"] = "functional"; headers["invoke.h"] = "functional"; headers["ranges_cmp.h"] = "functional"; diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 3e5b6c4142e..baf0290d655 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -205,6 +205,7 @@ bits_headers = \ ${bits_srcdir}/fs_ops.h \ ${bits_srcdir}/fs_path.h \ ${bits_srcdir}/fstream.tcc \ + ${bits_srcdir}/funcref_impl.h \ ${bits_srcdir}/funcwrap.h \ ${bits_srcdir}/gslice.h \ ${bits_srcdir}/gslice_array.h \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 3531162b5f7..e4e1079d8bd 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -559,6 +559,7 @@ bits_freestanding = \ @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}/funcref_impl.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/gslice.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/gslice_array.h \ @GLIBCXX_HOSTED_TRUE@ ${bits_srcdir}/hashtable.h \ diff --git a/libstdc++-v3/include/bits/funcref_impl.h b/libstdc++-v3/include/bits/funcref_impl.h new file mode 100644 index 00000000000..7d3ebe75faa --- /dev/null +++ b/libstdc++-v3/include/bits/funcref_impl.h @@ -0,0 +1,188 @@ +// Implementation of std::function_ref -*- 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/funcref_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 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + /// @cond undocumented + namespace __polyfunc + { + template<bool _Noex, typename _Ret, typename _Class, typename... _Args> + struct __skip_first_arg<_Ret(_Class::*)(_Args...) _GLIBCXX_MOF_CV + noexcept(_Noex)> + { using type = _Ret(_Args...) noexcept(_Noex); }; + + template<bool _Noex, typename _Ret, typename _Class, typename... _Args> + struct __skip_first_arg<_Ret(_Class::*)(_Args...) _GLIBCXX_MOF_CV& + noexcept(_Noex)> + { using type = _Ret(_Args...) noexcept(_Noex); }; + } // namespace __polyfunc + /// @endcond + + /** + * @brief Non-owning polymorphic function wrapper. + * @ingroup functors + * @since C++26 + * @headerfile functional + * + * The `std::function_ref` class template is a non-owning call wrapper, + * that refers a bound object. Using function_ref outside of lifetime + * of bound object has undefined behavior. + * + * It supports const-qualification and no-throw guarantees. The qualifications and + * exception-specification of the signature are respected when when invoking the + * reference function. + */ + template<typename _Res, typename... _ArgTypes, bool _Noex> + class function_ref<_Res(_ArgTypes...) _GLIBCXX_MOF_CV + noexcept(_Noex)> + { + using _Invoker = __polyfunc::_Invoker<_Noex, _Res, _ArgTypes...>; + using _Signature = _Invoker::_Signature; + + // [func.wrap.ref.ctor]/1 is-invokable-using + template<typename... _Tps> + static constexpr bool __is_invocable_using + = __conditional_t<_Noex, + is_nothrow_invocable_r<_Res, _Tps..., _ArgTypes...>, + is_invocable_r<_Res, _Tps..., _ArgTypes...>>::value; + + public: + /// Target and bound object is function pointed by parameter. + template<typename _Fn> + requires is_function_v<_Fn> && __is_invocable_using<_Fn*> + function_ref(_Fn* __fn) noexcept + { + __glibcxx_assert(__fn != nullptr); + this->_M_invoke = _Invoker::template _S_ptrs<_Fn*>(); + this->_M_ptrs._M_func = reinterpret_cast<void(*)()>(__fn); + } + + /// Target and bound object is object referenced by parameter. + template<typename _Fn, typename _Vt = remove_reference_t<_Fn>> + requires (!is_same_v<remove_cv_t<_Vt>, function_ref>) + && (!is_member_pointer_v<_Vt>) + // We deviate from standard by having this condition, that forces + // function references to use _Fn* constructors. This simplies + // implementation and provide better diagnostic when used in + // constant expression (above constructor is not constexpr). + && (!is_function_v<_Vt>) + && __is_invocable_using<_Vt _GLIBCXX_MOF_CV&> + constexpr + function_ref(_Fn&& __f) noexcept + { + this->_M_invoke = _Invoker::template _S_ptrs<_Vt _GLIBCXX_MOF_CV&>(); + this->_M_ptrs._M_obj = std::addressof(__f); + } + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4256. Incorrect constrains for function_ref constructors from nontype + /// Target object is __fn. There is no bound object. + template<auto __fn> + requires __is_invocable_using<const decltype(__fn)&> + constexpr + function_ref(nontype_t<__fn>) noexcept + { + using _Fn = remove_cv_t<decltype(__fn)>; + if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) + static_assert(__fn != nullptr); + + this->_M_invoke = &_Invoker::template _S_nttp<__fn>; + this->_M_ptrs._M_obj = nullptr; + } + + /// Target object is equivalent to std::bind_front<_fn>(std::ref(__ref)). + /// Bound object is object referenced by second parameter. + template<auto __fn, typename _Up, typename _Td = remove_reference_t<_Up>> + requires (!is_rvalue_reference_v<_Up&&>) + && __is_invocable_using<const decltype(__fn)&, _Td _GLIBCXX_MOF_CV&> + constexpr + function_ref(nontype_t<__fn>, _Up&& __ref) noexcept + { + using _Fn = remove_cv_t<decltype(__fn)>; + if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) + static_assert(__fn != nullptr); + + using _Tr = _Td _GLIBCXX_MOF_CV&; + if constexpr (is_member_pointer_v<_Fn> && is_lvalue_reference_v<_Tr>) + // N.B. invoking member pointer on lvalue produces the same effects, + // as invoking it on pointer to that lvalue. + this->_M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td _GLIBCXX_MOF_CV>; + else + this->_M_invoke = &_Invoker::template _S_bind_ref<__fn, _Tr>; + this->_M_ptrs._M_obj = std::addressof(__ref); + } + + /// Target object is equivalent to std::bind_front<_fn>(__ptr). + /// Bound object is object pointed by second parameter (if any). + template<auto __fn, typename _Td> + requires __is_invocable_using<const decltype(__fn)&, _Td _GLIBCXX_MOF_CV*> + constexpr + function_ref(nontype_t<__fn>, _Td _GLIBCXX_MOF_CV* __ptr) noexcept + { + using _Fn = remove_cv_t<decltype(__fn)>; + if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) + static_assert(__fn != nullptr); + if constexpr (is_member_pointer_v<_Fn>) + __glibcxx_assert(__ptr != nullptr); + + this->_M_invoke = &_Invoker::template _S_bind_ptr<__fn, _Td _GLIBCXX_MOF_CV>; + this->_M_ptrs._M_obj = __ptr; + } + + template<typename _Tp> + requires (!is_same_v<_Tp, function_ref>) + && (!is_pointer_v<_Tp>) && (!__is_nontype_v<_Tp>) + function_ref& + operator=(_Tp) = delete; + + /** Invoke the target object. + * + * The bound object will be invoked using the supplied arguments, + * and as const or non-const, as dictated by the template arguments + * of the `function_ref` specialization. + */ + _Res + operator()(_ArgTypes... __args) const noexcept(_Noex) + { return _M_invoke(this->_M_ptrs, std::forward<_ArgTypes>(__args)...); } + + private: + typename _Invoker::__ptrs_func_t _M_invoke; + __polyfunc::_Ptrs _M_ptrs; + }; + +#undef _GLIBCXX_MOF_CV + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std diff --git a/libstdc++-v3/include/bits/funcwrap.h b/libstdc++-v3/include/bits/funcwrap.h index 4e053534cf5..5c296d4cf51 100644 --- a/libstdc++-v3/include/bits/funcwrap.h +++ b/libstdc++-v3/include/bits/funcwrap.h @@ -1,4 +1,5 @@ -// Implementation of std::move_only_function and std::copyable_function -*- C++ -*- +// Implementation of std::move_only_function, std::copyable_function +// and std::function_ref -*- C++ -*- // Copyright The GNU Toolchain Authors. // @@ -36,7 +37,7 @@ #include <bits/version.h> -#if defined(__glibcxx_move_only_function) || defined(__glibcxx_copyable_function) +#if __glibcxx_move_only_function || __glibcxx_copyable_function || __glibcxx_function_ref #include <bits/invoke.h> #include <bits/utility.h> @@ -53,10 +54,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { union _Ptrs { - void* _M_obj; + const void* _M_obj; void (*_M_func)(); }; + template<typename _Tp> + [[__gnu__::__always_inline__]] + constexpr auto* + __cast_to(_Ptrs __ptrs) noexcept + { + using _Td = remove_reference_t<_Tp>; + if constexpr (is_const_v<_Td>) + return static_cast<_Td*>(__ptrs._M_obj); + else + return static_cast<_Td*>(const_cast<void*>(__ptrs._M_obj)); + } + struct _Storage { void* _M_addr() noexcept { return &_M_bytes[0]; } @@ -97,32 +110,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ::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 { @@ -143,6 +130,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _S_storage() { return &_S_call_storage<_Adjust_target<_Tp>>; } + using __ptrs_func_t = _Ret(*)(_Ptrs, _Args...) noexcept(_Noex); + template<typename _Tp> + static consteval __ptrs_func_t + _S_ptrs() + { return &_S_call_ptrs<_Adjust_target<_Tp>>; } + +#ifdef __glibcxx_function_ref // C++ >= 26 + template<auto __fn> + static _Ret + _S_nttp(_Ptrs, _Args... __args) noexcept(_Noex) + { return std::__invoke_r<_Ret>(__fn, std::forward<_Args>(__args)...); } + + template<auto __fn, typename _Tp> + static _Ret + _S_bind_ptr(_Ptrs __ptrs, _Args... __args) noexcept(_Noex) + { + auto __p = __polyfunc::__cast_to<_Tp>(__ptrs); + return std::__invoke_r<_Ret>(__fn, __p, std::forward<_Args>(__args)...); + } + + template<auto __fn, typename _Ref> + static _Ret + _S_bind_ref(_Ptrs __ptrs, _Args... __args) noexcept(_Noex) + { + auto __p = __polyfunc::__cast_to<_Ref>(__ptrs); + return std::__invoke_r<_Ret>(__fn, static_cast<_Ref>(*__p), + std::forward<_Args>(__args)...); + } +#endif // __glibcxx_function_ref + private: template<typename _Tp, typename _Td = remove_cvref_t<_Tp>> using _Adjust_target = @@ -152,8 +169,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION 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)...); + _Ptrs __ptrs; + if constexpr (is_function_v<remove_pointer_t<_Tp>>) + __ptrs._M_func = __ref._M_ptrs._M_func; + else if constexpr (!_Storage::_S_stored_locally<remove_cvref_t<_Tp>>()) + __ptrs._M_obj = __ref._M_ptrs._M_obj; + else + __ptrs._M_obj = __ref._M_addr(); + return _S_call_ptrs<_Tp>(__ptrs, std::forward<_Args>(__args)...); + } + + template<typename _Tp> + static _Ret + _S_call_ptrs(_Ptrs __ptrs, _Args... __args) noexcept(_Noex) + { + if constexpr (is_function_v<remove_pointer_t<_Tp>>) + return std::__invoke_r<_Ret>(reinterpret_cast<_Tp>(__ptrs._M_func), + std::forward<_Args>(__args)...); + else + { + auto __p = __polyfunc::__cast_to<_Tp>(__ptrs); + return std::__invoke_r<_Ret>(static_cast<_Tp>(*__p), + std::forward<_Args>(__args)...); + } } }; @@ -184,6 +222,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return false; } +#if __glibcxx_move_only_function || __glibcxx_copyable_function struct _Manager { enum class _Op @@ -241,7 +280,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION switch (__op) { case _Op::_Address: - __target._M_ptrs._M_obj = const_cast<void*>(__src->_M_addr()); + __target._M_ptrs._M_obj = __src->_M_addr(); return; case _Op::_Move: case _Op::_Copy: @@ -263,24 +302,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION switch (__op) { case _Op::_Address: - __target._M_ptrs._M_obj = __src->_M_ptr<_Tp>(); + __target._M_ptrs._M_obj = __src->_M_addr(); return; case _Op::_Move: { - _Tp* __obj = __src->_M_ptr<_Tp>(); + _Tp* __obj = static_cast<_Tp*>(const_cast<void*>(__src->_M_addr())); ::new(__target._M_addr()) _Tp(std::move(*__obj)); __obj->~_Tp(); } return; case _Op::_Destroy: - __target._M_ptr<_Tp>()->~_Tp(); + static_cast<_Tp*>(__target._M_addr())->~_Tp(); return; case _Op::_Copy: if constexpr (_Provide_copy) - ::new (__target._M_addr()) _Tp(__src->_M_ref<const _Tp&>()); - else - __builtin_unreachable(); - return; + { + auto* __obj = static_cast<const _Tp*>(__src->_M_addr()); + ::new (__target._M_addr()) _Tp(*__obj); + return; + } + __builtin_unreachable(); } } @@ -296,14 +337,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __target._M_ptrs._M_obj = __src->_M_ptrs._M_obj; return; case _Op::_Destroy: - delete __target._M_ptr<_Tp>(); + delete static_cast<const _Tp*>(__target._M_ptrs._M_obj); return; case _Op::_Copy: if constexpr (_Provide_copy) - __target._M_ptrs._M_obj = new _Tp(__src->_M_ref<const _Tp&>()); - else - __builtin_unreachable(); - return; + { + auto* __obj = static_cast<const _Tp*>(__src->_M_ptrs._M_obj); + __target._M_ptrs._M_obj = new _Tp(*__obj); + return; + } + __builtin_unreachable(); } } }; @@ -382,7 +425,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION friend class _Cpy_base; #endif // __glibcxx_copyable_function }; - +#endif // __glibcxx_copyable_function || __glibcxx_copyable_function } // namespace __polyfunc /// @endcond @@ -468,6 +511,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } // namespace __detail::__variant #endif // __glibcxx_copyable_function +#ifdef __glibcxx_function_ref // C++ >= 26 + /// @cond undocumented + namespace __polyfunc + { + template<typename _Sig> + struct __skip_first_arg; + + template<bool _Noex, typename _Ret, typename _Arg, typename... _Args> + struct __skip_first_arg<_Ret(*)(_Arg, _Args...) noexcept(_Noex)> + { using type = _Ret(_Args...) noexcept(_Noex); }; + + template<typename _Fn, typename _Tr> + consteval auto __deduce_funcref() + { + if constexpr (is_member_object_pointer_v<_Fn>) + // TODO Consider reporting issue to make this noexcept + return static_cast<invoke_result_t<_Fn, _Tr>(*)()>(nullptr); + else + return static_cast<__skip_first_arg<_Fn>::type*>(nullptr); + } + } // namespace __polyfunc + /// @endcond + + template<typename... _Signature> + class function_ref; // not defined + + template<typename _Fn> + requires is_function_v<_Fn> + function_ref(_Fn*) -> function_ref<_Fn>; + + template<auto __f, class _Fn = remove_pointer_t<decltype(__f)>> + requires is_function_v<_Fn> + function_ref(nontype_t<__f>) -> function_ref<_Fn>; + + template<auto __f, typename _Tp, class _Fn = decltype(__f)> + requires is_member_pointer_v<_Fn> || is_function_v<remove_pointer_t<_Fn>> + function_ref(nontype_t<__f>, _Tp&&) + -> function_ref< + remove_pointer_t<decltype(__polyfunc::__deduce_funcref<_Fn, _Tp&>())>>; + +#endif // __glibcxx_function_ref + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std @@ -503,5 +588,11 @@ _GLIBCXX_END_NAMESPACE_VERSION #include "cpyfunc_impl.h" #endif // __glibcxx_copyable_function -#endif // __glibcxx_copyable_function || __glibcxx_copyable_function +#ifdef __glibcxx_function_ref // C++ >= 26 +#include "funcref_impl.h" +#define _GLIBCXX_MOF_CV const +#include "funcref_impl.h" +#endif // __glibcxx_function_ref + +#endif // move_only_function || copyable_function || function_ref #endif // _GLIBCXX_FUNCWRAP_H diff --git a/libstdc++-v3/include/bits/utility.h b/libstdc++-v3/include/bits/utility.h index 6fa6b679c6d..84d25e0c658 100644 --- a/libstdc++-v3/include/bits/utility.h +++ b/libstdc++-v3/include/bits/utility.h @@ -316,6 +316,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION inline constexpr sorted_equivalent_t sorted_equivalent{}; #endif +#if __glibcxx_function_ref // >= C++26 + template<auto> + struct nontype_t + { + explicit nontype_t() = default; + }; + + template<auto __val> + constexpr nontype_t<__val> nontype{}; + + template<typename> + inline constexpr bool __is_nontype_v = false; + + template<auto __val> + inline constexpr bool __is_nontype_v<nontype_t<__val>> = true; +#endif + _GLIBCXX_END_NAMESPACE_VERSION } // namespace diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 6ca148f0488..55aab40d9bf 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1756,6 +1756,14 @@ ftms = { }; }; +ftms = { + name = function_ref; + values = { + v = 202306; + cxxmin = 26; + }; +}; + ftms = { name = out_ptr; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 48a090c14a3..026c9959482 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1958,6 +1958,16 @@ #endif /* !defined(__cpp_lib_copyable_function) && defined(__glibcxx_want_copyable_function) */ #undef __glibcxx_want_copyable_function +#if !defined(__cpp_lib_function_ref) +# if (__cplusplus > 202302L) +# define __glibcxx_function_ref 202306L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_function_ref) +# define __cpp_lib_function_ref 202306L +# endif +# endif +#endif /* !defined(__cpp_lib_function_ref) && defined(__glibcxx_want_function_ref) */ +#undef __glibcxx_want_function_ref + #if !defined(__cpp_lib_out_ptr) # if (__cplusplus >= 202100L) # define __glibcxx_out_ptr 202311L diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index 9a55b181e6d..307bcb95bcc 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -57,6 +57,7 @@ #define __glibcxx_want_bind_back #define __glibcxx_want_constexpr_functional #define __glibcxx_want_copyable_function +#define __glibcxx_want_function_ref #define __glibcxx_want_invoke #define __glibcxx_want_invoke_r #define __glibcxx_want_move_only_function @@ -86,7 +87,7 @@ # include <bits/ranges_cmp.h> // std::identity, ranges::equal_to etc. # include <compare> #endif -#if defined(__glibcxx_move_only_function) || defined(__glibcxx_copyable_function) +#if __glibcxx_move_only_function || __glibcxx_copyable_function || __glibcxx_function_ref # include <bits/funcwrap.h> #endif diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in index 417c8a1a562..ba468530338 100644 --- a/libstdc++-v3/src/c++23/std.cc.in +++ b/libstdc++-v3/src/c++23/std.cc.in @@ -1414,6 +1414,9 @@ export namespace std #endif #if __cpp_lib_copyable_function using std::copyable_function; +#endif +#if __cpp_lib_function_ref + using std::function_ref; #endif using std::multiplies; using std::negate; @@ -3195,6 +3198,10 @@ export namespace std using std::make_integer_sequence; using std::move; using std::move_if_noexcept; +#if __cpp_lib_function_ref + using std::nontype_t; + using std::nontype; +#endif using std::pair; using std::swap; using std::operator==; diff --git a/libstdc++-v3/testsuite/20_util/function_ref/assign.cc b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc new file mode 100644 index 00000000000..9b02dc49c2a --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc @@ -0,0 +1,108 @@ +// { dg-do compile { target c++26 } } + +#include <functional> + +#ifndef __cpp_lib_function_ref +# error "Feature-test macro for function_ref missing in <functional>" +#elif __cpp_lib_function_ref != 202306L +# error "Feature-test macro for function_ref has wrong value in <functional>" +#endif + +using std::nontype; +using std::nontype_t; +using std::function_ref; + +using std::is_nothrow_move_assignable_v; +using std::is_nothrow_copy_assignable_v; +using std::is_nothrow_assignable_v; +using std::is_assignable_v; +using std::is_nothrow_swappable_v; +using std::is_trivially_copyable_v; + +static_assert( is_nothrow_move_assignable_v<function_ref<void()>> ); +static_assert( is_nothrow_copy_assignable_v<function_ref<void()>> ); +static_assert( is_nothrow_swappable_v<function_ref<void()>> ); + +static_assert( ! is_assignable_v<function_ref<void()>, std::nullptr_t> ); + +static_assert( is_nothrow_assignable_v<function_ref<void()>, void()> ); +static_assert( is_nothrow_assignable_v<function_ref<void()>, void(&)()> ); +static_assert( is_nothrow_assignable_v<function_ref<void()>, void(*)()> ); +static_assert( is_nothrow_assignable_v<function_ref<void()>, int()> ); +static_assert( is_nothrow_assignable_v<function_ref<void()>, int(&)()> ); +static_assert( is_nothrow_assignable_v<function_ref<void()>, int(*)()> ); +static_assert( ! is_nothrow_assignable_v<function_ref<void()>, void(int)> ); +static_assert( is_nothrow_assignable_v<function_ref<void(int)>, void(int)> ); + +static_assert( is_nothrow_assignable_v<function_ref<void()>, + void() noexcept> ); +static_assert( is_nothrow_assignable_v<function_ref<void() noexcept>, + void() noexcept> ); +static_assert( ! is_assignable_v<function_ref<void() noexcept>, void() > ); + +struct S +{ + int x; + int f(); +}; +int funS(S); + +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + decltype(funS)> ); +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + decltype(&funS)> ); +static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::x)> ); +static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::f)> ); + +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + nontype_t<funS>> ); +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + nontype_t<&funS>> ); +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + nontype_t<&S::x>> ); +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + nontype_t<&S::f>> ); +struct Q +{ + void operator()() const; +}; + +static_assert( ! is_assignable_v<function_ref<void()>, Q> ); +static_assert( ! is_assignable_v<function_ref<void()>, Q&> ); +static_assert( ! is_assignable_v<function_ref<void()>, const Q&> ); +static_assert( ! is_assignable_v<function_ref<void() const>, Q> ); +static_assert( ! is_assignable_v<function_ref<void() const>, Q&> ); +static_assert( ! is_assignable_v<function_ref<void() const>, const Q&> ); + +static_assert( is_nothrow_assignable_v<function_ref<void()>, + nontype_t<Q{}>> ); +static_assert( is_nothrow_assignable_v<function_ref<void() const>, + nontype_t<Q{}>> ); + +constexpr bool +test_constexpr() +{ + function_ref<void(S)> fp(nontype<funS>); + fp = nontype<funS>; + fp = nontype<&funS>; + fp = nontype<&S::x>; + fp = nontype<&S::f>; + + constexpr Q cq; + function_ref<void() const> fq(cq); + fq = nontype<cq>; + return true; +} +static_assert( test_constexpr() ); + +void func(); + +void +test_instantiation() +{ + function_ref<void(S)> fp(funS); + fp = funS; + fp = &funS; + + test_constexpr(); +} diff --git a/libstdc++-v3/testsuite/20_util/function_ref/call.cc b/libstdc++-v3/testsuite/20_util/function_ref/call.cc new file mode 100644 index 00000000000..27231d1219b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/call.cc @@ -0,0 +1,144 @@ +// { dg-do run { target c++26 } } + +#include <functional> +#include <utility> +#include <testsuite_hooks.h> + +using std::nontype; +using std::function_ref; + +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<function_ref<void()>>> ); +static_assert( is_same_v<int, invoke_result_t<function_ref<int()>>> ); +static_assert( is_same_v<int&, invoke_result_t<function_ref<int&()>>> ); + +// Const qualier applies to target object +static_assert( is_invocable_v< function_ref<void()> const > ); +static_assert( is_invocable_v< function_ref<void()> const &> ); +static_assert( is_invocable_v< function_ref<void() const> > ); +static_assert( is_invocable_v< function_ref<void() const> &> ); +static_assert( is_invocable_v< function_ref<void() const> const > ); +static_assert( is_invocable_v< function_ref<void() const> const &> ); + +// With noexcept-specifier +static_assert( ! is_nothrow_invocable_v< function_ref<void()> > ); +static_assert( ! is_nothrow_invocable_v< function_ref<void() noexcept(false)> > ); +static_assert( is_nothrow_invocable_v< function_ref<void() noexcept> > ); + +void +test01() +{ + struct F + { + int operator()() { return 0; } + int operator()() const { return 1; } + }; + + function_ref<int()> f0{F{}}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + function_ref<int()> f1{nontype<F{}>}; + VERIFY( f1() == 1 ); + VERIFY( std::move(f1)() == 1 ); + + function_ref<int() const> f2{F{}}; + VERIFY( f2() == 1 ); + VERIFY( std::as_const(f2)() == 1 ); + VERIFY( std::move(f2)() == 1 ); + VERIFY( std::move(std::as_const(f2))() == 1 ); + + function_ref<int() const> f3{nontype<F{}>}; + VERIFY( f3() == 1 ); + VERIFY( std::as_const(f3)() == 1 ); + VERIFY( std::move(f3)() == 1 ); + VERIFY( std::move(std::as_const(f3))() == 1 ); +} + +void +test02() +{ + struct F + { + struct Arg {}; + int operator()(Arg& arg) const { return 0; } + int operator()(const Arg& arg) const { return 1; } + }; + F::Arg arg; + + function_ref<int()> f0{std::nontype<F{}>, arg}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + function_ref<int() const> f1{std::nontype<F{}>, arg}; + VERIFY( f1() == 1 ); + VERIFY( std::as_const(f1)() == 1 ); +} + +void +test03() +{ + struct F + { + struct Arg {}; + int operator()(Arg* arg) const { return 0; } + int operator()(const Arg* arg) const { return 1; } + }; + F::Arg arg; + + function_ref<int()> f0{std::nontype<F{}>, &arg}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + function_ref<int() const> f1{std::nontype<F{}>, &arg}; + VERIFY( f1() == 1 ); + VERIFY( std::as_const(f1)() == 1 ); +} + +void +test04() +{ + constexpr int (*fp)() = [] { return 0; }; + function_ref<int()> f0{fp}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + function_ref<int()> f1{nontype<fp>}; + VERIFY( f1() == 0 ); + VERIFY( std::move(f1)() == 0 ); + + const function_ref<int() const> f2{fp}; + VERIFY( f2() == 0 ); + VERIFY( std::move(f2)() == 0 ); + + const function_ref<int() const> f3{nontype<fp>}; + VERIFY( f2() == 0 ); + VERIFY( std::move(f2)() == 0 ); +} + +struct Incomplete; + +void +test_params() +{ + auto f = [](auto&&) {}; + // There is discussion if this is supported. + // std::function_ref<void(Incomplete)> f1(f); + std::function_ref<void(Incomplete&)> f2(f); + // See PR120259, this should be supported. + // std::function_ref<void(Incomplete&&)> f3(f); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test_params(); +} diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons.cc b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc new file mode 100644 index 00000000000..a91f5ba3dab --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc @@ -0,0 +1,218 @@ +// { dg-do compile { target c++26 } } +// { dg-add-options no_pch } + +#include <functional> + +#ifndef __cpp_lib_function_ref +# error "Feature-test macro for function_ref missing in <functional>" +#elif __cpp_lib_function_ref != 202306L +# error "Feature-test macro for function_ref has wrong value in <functional>" +#endif + +using std::nontype; +using std::nontype_t; +using std::function_ref; + +using std::is_default_constructible_v; +using std::is_nothrow_copy_constructible_v; +using std::is_nothrow_move_constructible_v; +using std::is_nothrow_constructible_v; +using std::is_constructible_v; +using std::is_trivially_copyable_v; + +static_assert( ! is_default_constructible_v<function_ref<void()>> ); +static_assert( is_nothrow_move_constructible_v<function_ref<void()>> ); +static_assert( is_nothrow_copy_constructible_v<function_ref<void()>> ); +static_assert( is_trivially_copyable_v<function_ref<void()>> ); + +static_assert( ! is_constructible_v<function_ref<void()>, std::nullptr_t> ); + +static_assert( is_nothrow_constructible_v<function_ref<void()>, void()> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, void(&)()> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, void(*)()> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, int()> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, int(&)()> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, int(*)()> ); +static_assert( ! is_constructible_v<function_ref<void()>, void(int)> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, void(int)> ); + +static_assert( is_nothrow_constructible_v<function_ref<void()>, + void() noexcept> ); +static_assert( is_nothrow_constructible_v<function_ref<void() noexcept>, + void() noexcept> ); +static_assert( ! is_constructible_v<function_ref<void() noexcept>, + void() > ); + +struct S +{ + int x; + int f(); +}; +int funS(S); + +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + decltype(funS)> ); +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + decltype(&funS)> ); +static_assert( ! is_constructible_v<function_ref<int(S)>, + decltype(&S::x)> ); +static_assert( ! is_constructible_v<function_ref<int(S)>, + decltype(&S::f)> ); + +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + nontype_t<funS>> ); +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + nontype_t<&funS>> ); +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + nontype_t<&S::x>> ); +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + nontype_t<&S::f>> ); + +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<funS>, S&> ); +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<&funS>, S&> ); +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<&S::x>, S&> ); +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<&S::f>, S&> ); + +static_assert( ! is_constructible_v<function_ref<int()>, + nontype_t<funS>, S*> ); +static_assert( ! is_constructible_v<function_ref<int()>, + nontype_t<&funS>, S*> ); +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<&S::x>, S*> ); +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<&S::f>, S*> ); + +struct M +{ + void operator()(); +}; + + +static_assert( is_nothrow_constructible_v<function_ref<void()>, M> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, M&> ); +static_assert( ! is_constructible_v<function_ref<void()>, const M&> ); +static_assert( ! is_constructible_v<function_ref<void() const>, M> ); +static_assert( ! is_constructible_v<function_ref<void() const>, const M&> ); +static_assert( ! is_constructible_v<function_ref<void()>, + nontype_t<M{}>> ); +static_assert( ! is_constructible_v<function_ref<void() const>, + nontype_t<M{}>> ); +struct Q +{ + void operator()(int) const; + void operator()(int*) const; +}; + +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q&> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, const Q&> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, Q> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, Q&> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, const Q&> ); + +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, + nontype_t<Q{}>> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, + nontype_t<Q{}>> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, + nontype_t<Q{}>, int&> ); +static_assert( is_nothrow_constructible_v<function_ref<void() const>, + nontype_t<Q{}>, int&> ); +static_assert( ! is_constructible_v<function_ref<void()>, + nontype_t<Q{}>, int> ); +static_assert( ! is_constructible_v<function_ref<void() const>, + nontype_t<Q{}>, int> ); + +static_assert( is_nothrow_constructible_v<function_ref<void()>, + nontype_t<Q{}>, int*> ); +static_assert( ! is_constructible_v<function_ref<void() const>, + nontype_t<Q{}>, int*> ); + +struct L +{ + void operator()() &; +}; + +static_assert( is_nothrow_constructible_v<function_ref<void()>, L> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, L&> ); +static_assert( ! is_constructible_v<function_ref<void()>, const L&> ); +static_assert( ! is_constructible_v<function_ref<void() const>, L> ); +static_assert( ! is_constructible_v<function_ref<void() const>, const L&> ); +static_assert( ! is_constructible_v<function_ref<void()>, + nontype_t<L{}>> ); +static_assert( ! is_constructible_v<function_ref<void() const>, + nontype_t<L{}>> ); + +struct R +{ + void operator()(float) const&&; +}; + +static_assert( ! is_constructible_v<function_ref<void(float)>, R> ); +static_assert( ! is_constructible_v<function_ref<void(float)>, R&> ); +static_assert( ! is_constructible_v<function_ref<void(float) const>, R> ); +static_assert( ! is_constructible_v<function_ref<void(float) const>, R&> ); +static_assert( ! is_constructible_v<function_ref<void(float) const>, const R&> ); + +static_assert( ! is_constructible_v<function_ref<void(float)>, + nontype_t<R{}>> ); +static_assert( ! is_constructible_v<function_ref<void(float) const>, + nontype_t<R{}>> ); + +constexpr bool +test_constexpr() +{ + function_ref<void(S)> fp1(nontype<funS>); + function_ref<void(S)> fp3(nontype<&funS>); + function_ref<void(S)> fp4(nontype<&S::x>); + function_ref<void(S)> fp5(nontype<&S::f>); + + S s; + function_ref<void()> fp6(nontype<&funS>, s); + function_ref<void()> fp7(nontype<&S::x>, s); + function_ref<void()> fp8(nontype<&S::x>, &s); + function_ref<void()> fp9(nontype<&S::f>, s); + function_ref<void()> fp10(nontype<&S::f>, &s); + + M m; + function_ref<void()> fm1(m); + function_ref<void()> fm2(std::move(m)); + + Q q; + constexpr Q cq; + function_ref<void(int)> fq1(q); + function_ref<void(int) const> fq2(q); + function_ref<void(int) const> fq3(std::move(q)); + + function_ref<void(int)> fcq1(cq); + function_ref<void(int) const> f(cq); + function_ref<void(int)> fcq3(nontype<cq>); + function_ref<void(int) const> fcq4(nontype<cq>); + + int i = 0; + function_ref<void()> fcq5(nontype<cq>, i); + function_ref<void() const> fcq6(nontype<cq>, i); + function_ref<void()> fcq7(nontype<cq>, &i); + + L l; + function_ref<void()> fl1(l); + function_ref<void()> fl2(std::move(l)); + + return true; +} +static_assert( test_constexpr() ); + +void func(); + +void +test_instantiation() +{ + function_ref<void(S)> fp1(funS); + function_ref<void(S)> fp2(&funS); + + test_constexpr(); +} diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc new file mode 100644 index 00000000000..050090df370 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc @@ -0,0 +1,30 @@ +// { dg-do compile { target c++26 } } + +#include <functional> + +using std::nontype; +using std::function_ref; + +struct S +{ + int x; + void foo(); +}; +S s; + +constexpr int(*fp)(S) = nullptr; +constexpr int S::*mdp = nullptr; +constexpr int (S::*mfp)() = nullptr; + +function_ref<int(S)> fd1(nontype<fp>); // { dg-error "from here" } +function_ref<int(S)> fd2(nontype<mdp>); // { dg-error "from here" } +function_ref<int(S)> fd3(nontype<mfp>); // { dg-error "from here" } + +function_ref<int()> br4(nontype<fp>, s); // { dg-error "from here" } +function_ref<int()> br5(nontype<mdp>, s); // { dg-error "from here" } +function_ref<int()> br6(nontype<mfp>, s); // { dg-error "from here" } + +function_ref<int()> bp7(nontype<mdp>, &s); // { dg-error "from here" } +function_ref<int()> bp8(nontype<mfp>, &s); // { dg-error "from here" } + +// { dg-prune-output "static assertion failed" } diff --git a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc new file mode 100644 index 00000000000..49ff0a78829 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc @@ -0,0 +1,152 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <testsuite_hooks.h> + +using std::function_ref; + +static_assert( std::is_constructible_v<std::function_ref<void() const>, + std::function_ref<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; + +// The C++26 [func.wrap.general] p2 does not currently cover funciton_ref, +// so we make extra copies of arguments. + +void +test01() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function_ref<int(CountedArg) const noexcept> r1(f); + std::move_only_function<int(CountedArg) const noexcept> m1(f); + std::copyable_function<int(CountedArg) const noexcept> c1(f); + + // Complatible signatures + std::function_ref<int(CountedArg) const noexcept> r2m(m1); + VERIFY( r2m(c) == 2 ); + std::function_ref<int(CountedArg) const noexcept> r2c(c1); + VERIFY( r2c(c) == 2 ); + + std::function_ref<int(CountedArg) const> r3r(r1); + VERIFY( r3r(c) == 2 ); + std::function_ref<int(CountedArg) const> r3m(m1); + VERIFY( r3m(c) == 2 ); + std::function_ref<int(CountedArg) const> r3c(c1); + VERIFY( r3c(c) == 2 ); + + std::function_ref<int(CountedArg)> r4r(r1); + VERIFY( r4r(c) == 2 ); + std::function_ref<int(CountedArg)> r4m(m1); + VERIFY( r4m(c) == 2 ); + std::function_ref<int(CountedArg)> r4c(c1); + VERIFY( r4c(c) == 2 ); + + // Incompatible signatures + std::function_ref<long(CountedArg) const noexcept> r5r(r1); + VERIFY( r5r(c) == 2 ); + std::function_ref<long(CountedArg) const noexcept> r5m(m1); + VERIFY( r5r(c) == 2 ); + std::function_ref<long(CountedArg) const noexcept> r5c(c1); + VERIFY( r5r(c) == 2 ); +} + +void +test02() +{ + // Constructing move_only_function and copyable_function from function_ref, + // have not chance to restore manager, so we store function_ref inside. + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function_ref<int(CountedArg) const noexcept> r1(f); + + std::move_only_function<int(CountedArg) const noexcept> m1(r1); + VERIFY( m1(c) == 2 ); + + std::copyable_function<int(CountedArg) const noexcept> c1(r1); + VERIFY( c1(c) == 2 ); +} + +void +test03() +{ + 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::function_ref<int(CountedArg) const> r1(f); + VERIFY( r1(c) == 1001 ); + + // Call const overload as std::function_ref<int(CountedArg) const> + // inside std::function_ref<int(CountedArg)> would do. + std::function_ref<int(CountedArg)> r2(r1); + VERIFY( r2(c) == 1002 ); + std::move_only_function<int(CountedArg)> m2(r1); + VERIFY( m2(c) == 1002 ); + + // Call non-const overload as const-qualifed operator() for + // std::function_ref<int(CountedArg)> do. + std::function_ref<int(CountedArg)> r3(f); + VERIFY( r3(c) == 1 ); + std::function_ref<int(CountedArg) const> r4(r3); + VERIFY( r4(c) == 2 ); + std::move_only_function<int(CountedArg) const> m4(r3); + VERIFY( m4(c) == 2 ); +} + +void +test04() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function_ref<int(CountedArg)> w1(f); + // function_ref stores function_ref due incompatibile signatures + std::function_ref<int(CountedArg const&)> w2(std::move(w1)); + // copy is made when passing to int(CountedArg) + VERIFY( w2(c) == 1 ); + // wrapped 3 times + std::function_ref<int(CountedArg)> w3(w2); + VERIFY( w3(c) == 2 ); + // wrapped 4 times + std::function_ref<int(CountedArg const&)> w4(w3); + VERIFY( w4(c) == 2 ); + // wrapped 5 times + std::function_ref<int(CountedArg)> w5(w4); + VERIFY( w5(c) == 3 ); +} + +void +test05() +{ + // No special interoperability with std::function + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function<int(CountedArg)> f1(f); + std::function_ref<int(CountedArg) const> c1(std::move(f1)); + VERIFY( c1(c) == 2 ); + + std::function_ref<int(CountedArg) const> c2(f); + std::function<int(CountedArg)> f2(c2); + VERIFY( f2(c) == 2 ); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); +} diff --git a/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc new file mode 100644 index 00000000000..2940b876954 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc @@ -0,0 +1,103 @@ +// { dg-do compile { target c++26 } } + +#include <functional> +#include <type_traits> + +using std::is_same_v; +using std::nontype; +using std::nontype_t; +using std::function_ref; + +int i = 0; + +void f0(); +void f0n() noexcept; + +static_assert( is_same_v<decltype(function_ref(f0)), + function_ref<void()>> ); +static_assert( is_same_v<decltype(function_ref(f0n)), + function_ref<void() noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f0>)), + function_ref<void()>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f0n>)), + function_ref<void() noexcept>> ); + +void f1(int); +void f1n(int) noexcept; + +static_assert( is_same_v<decltype(function_ref(f1)), + function_ref<void(int)>> ); +static_assert( is_same_v<decltype(function_ref(f1n)), + function_ref<void(int) noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f1>)), + function_ref<void(int)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f1n>)), + function_ref<void(int) noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f1>, i)), + function_ref<void()>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f1n>, i)), + function_ref<void() noexcept>> ); + +void f2(int*, int); +void f2n(int*, int) noexcept; + +static_assert( is_same_v<decltype(function_ref(f2)), + function_ref<void(int*, int)>> ); +static_assert( is_same_v<decltype(function_ref(f2n)), + function_ref<void(int*, int) noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f2>)), + function_ref<void(int*, int)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f2n>)), + function_ref<void(int*, int) noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f2>, &i)), + function_ref<void(int)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f2n>, &i)), + function_ref<void(int) noexcept>> ); + +struct S +{ + int mem; + int f(); + int fn() noexcept; + + int fc(int) const; + int fcn(int) const noexcept; + + int fl(int) &; + int fln(int) & noexcept; + + int fcl(float) const&; + int fcln(float) const& noexcept; +}; +S s{}; +const S cs{}; + +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, s)), + function_ref<int&()>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, cs)), + function_ref<const int&()>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &s)), + function_ref<int&()>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &cs)), + function_ref<const int&()>> ); + +static_assert( is_same_v<decltype(function_ref(nontype<&S::f>, s)), + function_ref<int()>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::fn>, &s)), + function_ref<int() noexcept>> ); + +static_assert( is_same_v<decltype(function_ref(nontype<&S::fc>, &s)), + function_ref<int(int)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::fcn>, s)), + function_ref<int(int) noexcept>> ); + +static_assert( is_same_v<decltype(function_ref(nontype<&S::fl>, &s)), + function_ref<int(int)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::fln>, s)), + function_ref<int(int) noexcept>> ); + +static_assert( is_same_v<decltype(function_ref(nontype<&S::fcl>, s)), + function_ref<int(float)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::fcln>, &s)), + function_ref<int(float) noexcept>> ); + -- 2.49.0