[libcxx-commits] [libcxx] [libc++] P0792R14: `std::function_ref` (PR #94894)
Xiaoyang Liu via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Jun 9 03:56:12 PDT 2024
https://github.com/xiaoyang-sde updated https://github.com/llvm/llvm-project/pull/94894
>From d3bf8ef262cc3814a382cfcfcfbffa61ca6812dd Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 17:32:42 -0700
Subject: [PATCH 1/6] [libc++] P0792R14: 'function_ref'
---
libcxx/include/CMakeLists.txt | 4 +
libcxx/include/__functional/function_ref.h | 46 +++++
.../__functional/function_ref_common.h | 72 +++++++
.../include/__functional/function_ref_impl.h | 181 ++++++++++++++++++
libcxx/include/__utility/nontype.h | 34 ++++
libcxx/include/functional | 1 +
libcxx/include/module.modulemap | 2 +
libcxx/include/utility | 10 +
.../func.wrap.ref/call/const.pass.cpp | 124 ++++++++++++
.../call/const_noexcept.pass.cpp | 124 ++++++++++++
.../func.wrap.ref/call/default.pass.cpp | 119 ++++++++++++
.../func.wrap.ref/call/noexcept.pass.cpp | 119 ++++++++++++
12 files changed, 836 insertions(+)
create mode 100644 libcxx/include/__functional/function_ref.h
create mode 100644 libcxx/include/__functional/function_ref_common.h
create mode 100644 libcxx/include/__functional/function_ref_impl.h
create mode 100644 libcxx/include/__utility/nontype.h
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cfe1f44777bca..6dd28b79117af 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -393,6 +393,9 @@ set(files
__functional/compose.h
__functional/default_searcher.h
__functional/function.h
+ __functional/function_ref_common.h
+ __functional/function_ref_impl.h
+ __functional/function_ref.h
__functional/hash.h
__functional/identity.h
__functional/invoke.h
@@ -855,6 +858,7 @@ set(files
__utility/is_valid_range.h
__utility/move.h
__utility/no_destroy.h
+ __utility/nontype.h
__utility/pair.h
__utility/piecewise_construct.h
__utility/priority_tag.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..01cc22a64018e
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.h
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+// NOLINTBEGIN(readability-duplicate-include)
+
+#define _LIBCPP_FUNCTION_REF_CV
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV const
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV const
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+// NOLINTEND(readability-duplicate-include)
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
new file mode 100644
index 0000000000000..f7d5575ebaffd
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+
+#include <__config>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/nontype.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class>
+inline constexpr bool __is_function_ref = false;
+
+template <class _Rp, class... _ArgTypes>
+inline constexpr bool __is_function_ref<function_ref<_Rp, _ArgTypes...>> = true;
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind;
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...), _Tp> {
+ using type = _Rp(_ArgTypes...);
+};
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
+ using type = _Rp(_ArgTypes...) noexcept;
+};
+
+template <class _Tp, class _Mp, class _Gp>
+ requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+ using type = invoke_result_t<_Mp _Gp::*, _Tp&>;
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+ requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Fn>
+ requires is_function_v<remove_pointer_t<decltype(_Fn)>>
+function_ref(nontype_t<_Fn>) -> function_ref<remove_pointer_t<decltype(_Fn)>>;
+
+template <auto _Fn, class _Tp>
+function_ref(nontype_t<_Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<decltype(_Fn), _Tp&>>;
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
new file mode 100644
index 0000000000000..4e1cd13a02ae2
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/forward.h>
+#include <__utility/nontype.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class _Rp, class... _ArgTypes>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT)> {
+private:
+# if _LIBCPP_FUNCTION_REF_NOEXCEPT == true
+ template <class... _Tp>
+ static constexpr bool __is_invocable_using = is_nothrow_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
+# else
+ template <class... _Tp>
+ static constexpr bool __is_invocable_using = is_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
+# endif
+
+ union __storage_t {
+ void* __obj_ptr;
+ void const* __obj_const_ptr;
+ void (*__fn_ptr)();
+
+ constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {};
+
+ template <class _Tp>
+ constexpr explicit __storage_t(_Tp* __ptr) noexcept {
+ if constexpr (is_object_v<_Tp>) {
+ if constexpr (is_const_v<_Tp>) {
+ __obj_const_ptr = __ptr;
+ } else {
+ __obj_ptr = __ptr;
+ }
+ } else {
+ static_assert(is_function_v<_Tp>);
+ __fn_ptr = reinterpret_cast<void (*)()>(__ptr);
+ }
+ }
+ } __storage_;
+
+ template <class _Tp>
+ _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_t __storage) {
+ if constexpr (is_object_v<_Tp>) {
+ if constexpr (is_const_v<_Tp>) {
+ return static_cast<_Tp*>(__storage.__obj_const_ptr);
+ } else {
+ return static_cast<_Tp*>(__storage.__obj_ptr);
+ }
+ } else {
+ static_assert(is_function_v<_Tp>);
+ return reinterpret_cast<_Tp*>(__storage.__fn_ptr);
+ }
+ }
+
+ using __vtable_call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+ __vtable_call_t __vtable_call_;
+
+public:
+ template <class _Fp>
+ requires is_function_v<_Fp> && __is_invocable_using<_Fp>
+ _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+ : __storage_(__fn_ptr),
+ __vtable_call_(
+ [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
+ }) {
+ _LIBCPP_ASSERT_UNCATEGORIZED(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+ }
+
+ template <class _Fp, class _Tp = remove_reference_t<_Fp>>
+ requires(!__is_function_ref<remove_cvref_t<_Fp>> && !is_member_pointer_v<_Tp> &&
+ __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&>)
+ _LIBCPP_HIDE_FROM_ABI function_ref(_Fp&& __obj) noexcept
+ : __storage_(std::addressof(__obj)),
+ __vtable_call_(
+ [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+ return __obj(std::forward<_ArgTypes>(__args)...);
+ }) {}
+
+ template <auto _Fn>
+ requires __is_invocable_using<decltype(_Fn)>
+ constexpr function_ref(nontype_t<_Fn>) noexcept
+ : __vtable_call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
+ }) {
+ if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+ static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+ }
+ }
+
+ template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+ requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
+ constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
+ : __storage_(std::addressof(__obj)),
+ __vtable_call_(
+ [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+ return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+ }) {
+ if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+ static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+ }
+ }
+
+ template <auto _Fn, class _Tp>
+ requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
+ constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+ : __storage_(__obj_ptr),
+ __vtable_call_(
+ [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+ return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+ }) {
+ if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+ static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+ }
+
+ if constexpr (is_member_pointer_v<decltype(_Fn)>) {
+ _LIBCPP_ASSERT_UNCATEGORIZED(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
+ }
+ }
+
+ constexpr function_ref(const function_ref&) noexcept = default;
+
+ constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+ template <class _Tp>
+ requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_nontype_t<_Tp>)
+ function_ref& operator=(_Tp) = delete;
+
+ constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
+ return __vtable_call_(__storage_, std::forward<_ArgTypes>(__args)...);
+ }
+};
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
+ _Tp> {
+ using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+};
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
+ _Tp> {
+ using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/nontype.h
new file mode 100644
index 0000000000000..86893f17e2d96
--- /dev/null
+++ b/libcxx/include/__utility/nontype.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___UTILITY_NONTYPE_H
+#define _LIBCPP___UTILITY_NONTYPE_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <auto _Vp>
+struct nontype_t {
+ explicit nontype_t() = default;
+};
+
+template <auto _Vp>
+inline constexpr nontype_t<_Vp> nontype{};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_NONTYPE_H
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 27cf21e1a4c8b..f413e296fab9e 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -541,6 +541,7 @@ POLICY: For non-variadic implementations, the number of arguments is limited
#include <__functional/compose.h>
#include <__functional/default_searcher.h>
#include <__functional/function.h>
+#include <__functional/function_ref.h>
#include <__functional/hash.h>
#include <__functional/identity.h>
#include <__functional/invoke.h>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 48391b2a12095..5143f3fadfdc2 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1339,6 +1339,7 @@ module std_private_functional_compose [system] {
}
module std_private_functional_default_searcher [system] { header "__functional/default_searcher.h" }
module std_private_functional_function [system] { header "__functional/function.h" }
+module std_private_functional_function_ref [system] { header "__functional/function_ref.h" }
module std_private_functional_hash [system] {
header "__functional/hash.h"
export std_cstdint
@@ -2073,6 +2074,7 @@ module std_private_utility_move [system] {
export std_private_type_traits_remove_reference
}
module std_private_utility_no_destroy [system] { header "__utility/no_destroy.h" }
+module std_private_utility_nontype [system] { header "__utility/nontype.h" }
module std_private_utility_pair [system] {
header "__utility/pair.h"
export std_private_ranges_subrange_fwd
diff --git a/libcxx/include/utility b/libcxx/include/utility
index 90713da621c5d..07dba7593f2df 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -238,6 +238,15 @@ template <size_t I>
template <size_t I>
inline constexpr in_place_index_t<I> in_place_index{};
+// nontype argument tag
+template<auto V>
+ struct nontype_t {
+ explicit nontype_t() = default;
+ };
+template<auto V> constexpr nontype_t<V> nontype{};
+
+template<auto V> constexpr nontype_t<V> nontype{};
+
// [utility.underlying], to_underlying
template <class T>
constexpr underlying_type_t<T> to_underlying( T value ) noexcept; // C++23
@@ -259,6 +268,7 @@ template <class T>
#include <__utility/in_place.h>
#include <__utility/integer_sequence.h>
#include <__utility/move.h>
+#include <__utility/nontype.h>
#include <__utility/pair.h>
#include <__utility/piecewise_construct.h>
#include <__utility/priority_tag.h>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
new file mode 100644
index 0000000000000..53d7a5922353d
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void() const>&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const>>);
+static_assert(std::is_invocable_v<std::function_ref<void() const>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const> const>);
+static_assert(std::is_invocable_v<std::function_ref<void() const> const&&>);
+
+int fn() { return 42; }
+
+struct {
+ int operator()() const { return 42; }
+} fn_obj;
+
+void test() {
+ // template<class F> function_ref(F* f) noexcept;
+ {
+ // initialized from a function
+ std::function_ref<int() const> fn_ref = fn;
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer
+ std::function_ref<int() const> fn_ref = &fn;
+ assert(fn_ref() == 42);
+ }
+
+ // template<class F> constexpr function_ref(F&& f) noexcept;
+ {
+ // initialized from a function object
+ std::function_ref<int() const> fn_ref = fn_obj;
+ assert(fn_ref() == 42);
+ }
+}
+
+struct S {
+ int data_mem = 42;
+
+ int fn_mem() const { return 42; }
+};
+
+void test_nontype_t() {
+ // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+ {
+ // initialized from a function through `nontype_t`
+ std::function_ref<int() const> fn_ref = std::nontype_t<fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer through `nontype_t`
+ std::function_ref<int() const> fn_ref = std::nontype_t<&fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function object through `nontype_t`
+ std::function_ref<int() const> fn_ref = std::nontype_t<fn_obj>();
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t`
+ std::function_ref<int(S) const> fn_ref = std::nontype_t<&S::data_mem>();
+ assert(fn_ref(s) == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t`
+ std::function_ref<int(S) const> fn_ref = std::nontype_t<&S::fn_mem>();
+ assert(fn_ref(s) == 42);
+ }
+
+ // template<auto f, class U>
+ // constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() const> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() const> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+
+ // template<auto f, class T>
+ // constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+ std::function_ref<int() const> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ static_assert(std::is_same_v<decltype(&s), S*>);
+ std::function_ref<int() const> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+}
+
+int main(int, char**) {
+ test();
+ test_nontype_t();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
new file mode 100644
index 0000000000000..18d44e4b7ef0b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const >);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const&&>);
+
+int fn() noexcept { return 42; }
+
+struct {
+ int operator()() const noexcept { return 42; }
+} fn_obj;
+
+void test() {
+ // template<class F> function_ref(F* f) noexcept;
+ {
+ // initialized from a function
+ std::function_ref<int() const noexcept> fn_ref = fn;
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer
+ std::function_ref<int() const noexcept> fn_ref = &fn;
+ assert(fn_ref() == 42);
+ }
+
+ // template<class F> const noexceptexpr function_ref(F&& f) noexcept;
+ {
+ // initialized from a function object
+ std::function_ref<int() const noexcept> fn_ref = fn_obj;
+ assert(fn_ref() == 42);
+ }
+}
+
+struct S {
+ int data_mem = 42;
+
+ int fn_mem() const noexcept { return 42; }
+};
+
+void test_nontype_t() {
+ // template<auto f> const noexceptexpr function_ref(nontype_t<f>) noexcept;
+ {
+ // initialized from a function through `nontype_t`
+ std::function_ref<int() const noexcept> fn_ref = std::nontype_t<fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer through `nontype_t`
+ std::function_ref<int() const noexcept> fn_ref = std::nontype_t<&fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function object through `nontype_t`
+ std::function_ref<int() const noexcept> fn_ref = std::nontype_t<fn_obj>();
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t`
+ std::function_ref<int(S) const noexcept> fn_ref = std::nontype_t<&S::data_mem>();
+ assert(fn_ref(s) == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t`
+ std::function_ref<int(S) const noexcept> fn_ref = std::nontype_t<&S::fn_mem>();
+ assert(fn_ref(s) == 42);
+ }
+
+ // template<auto f, class U>
+ // const noexceptexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+
+ // template<auto f, class T>
+ // const noexceptexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+ std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ static_assert(std::is_same_v<decltype(&s), S*>);
+ std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+}
+
+int main(int, char**) {
+ test();
+ test_nontype_t();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
new file mode 100644
index 0000000000000..ebc07a6c3520c
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void()>&>);
+static_assert(std::is_invocable_v<std::function_ref<void()>>);
+static_assert(std::is_invocable_v<std::function_ref<void()>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void()> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void()> const>);
+static_assert(std::is_invocable_v<std::function_ref<void()> const&&>);
+
+int fn() { return 42; }
+
+struct {
+ int operator()() { return 42; }
+} fn_obj;
+
+void test() {
+ // template<class F> function_ref(F* f) noexcept;
+ {
+ // initialized from a function
+ std::function_ref<int()> fn_ref = fn;
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer
+ std::function_ref<int()> fn_ref = &fn;
+ assert(fn_ref() == 42);
+ }
+
+ // template<class F> constexpr function_ref(F&& f) noexcept;
+ {
+ // initialized from a function object
+ std::function_ref<int()> fn_ref = fn_obj;
+ assert(fn_ref() == 42);
+ }
+}
+
+struct S {
+ int data_mem = 42;
+
+ int fn_mem() { return 42; }
+};
+
+void test_nontype_t() {
+ // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+ {
+ // initialized from a function through `nontype_t`
+ std::function_ref<int()> fn_ref = std::nontype_t<fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer through `nontype_t`
+ std::function_ref<int()> fn_ref = std::nontype_t<&fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t`
+ std::function_ref<int(S)> fn_ref = std::nontype_t<&S::data_mem>();
+ assert(fn_ref(s) == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t`
+ std::function_ref<int(S)> fn_ref = std::nontype_t<&S::fn_mem>();
+ assert(fn_ref(s) == 42);
+ }
+
+ // template<auto f, class U>
+ // constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int()> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int()> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+
+ // template<auto f, class T>
+ // constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+ std::function_ref<int()> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ static_assert(std::is_same_v<decltype(&s), S*>);
+ std::function_ref<int()> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+}
+
+int main(int, char**) {
+ test();
+ test_nontype_t();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp
new file mode 100644
index 0000000000000..9b58e677ed6ba
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept>&>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const&&>);
+
+int fn() noexcept { return 42; }
+
+struct {
+ int operator()() noexcept { return 42; }
+} fn_obj;
+
+void test() {
+ // template<class F> function_ref(F* f) noexcept;
+ {
+ // initialized from a function
+ std::function_ref<int() noexcept> fn_ref = fn;
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer
+ std::function_ref<int() noexcept> fn_ref = &fn;
+ assert(fn_ref() == 42);
+ }
+
+ // template<class F> constexpr function_ref(F&& f) noexcept;
+ {
+ // initialized from a function object
+ std::function_ref<int() noexcept> fn_ref = fn_obj;
+ assert(fn_ref() == 42);
+ }
+}
+
+struct S {
+ int data_mem = 42;
+
+ int fn_mem() noexcept { return 42; }
+};
+
+void test_nontype_t() {
+ // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+ {
+ // initialized from a function through `nontype_t`
+ std::function_ref<int() noexcept> fn_ref = std::nontype_t<fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer through `nontype_t`
+ std::function_ref<int() noexcept> fn_ref = std::nontype_t<&fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t`
+ std::function_ref<int(S) noexcept> fn_ref = std::nontype_t<&S::data_mem>();
+ assert(fn_ref(s) == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t`
+ std::function_ref<int(S) noexcept> fn_ref = std::nontype_t<&S::fn_mem>();
+ assert(fn_ref(s) == 42);
+ }
+
+ // template<auto f, class U>
+ // constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+
+ // template<auto f, class T>
+ // constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+ std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ static_assert(std::is_same_v<decltype(&s), S*>);
+ std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+}
+
+int main(int, char**) {
+ test();
+ test_nontype_t();
+ return 0;
+}
>From d8ab8e551502f6f95acf140495b9e22e12e624c0 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 23:22:06 -0700
Subject: [PATCH 2/6] [libc++] P0792R14: 'function_ref'
---
.../__functional/function_ref_common.h | 8 +-
.../include/__functional/function_ref_impl.h | 48 +++++------
.../func.wrap.ref/ctor/ctad.pass.cpp | 84 +++++++++++++++++++
.../{call => invoke}/const.pass.cpp | 0
.../{call => invoke}/const_noexcept.pass.cpp | 0
.../{call => invoke}/default.pass.cpp | 0
.../{call => invoke}/noexcept.pass.cpp | 0
7 files changed, 111 insertions(+), 29 deletions(-)
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/const.pass.cpp (100%)
rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/const_noexcept.pass.cpp (100%)
rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/default.pass.cpp (100%)
rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/noexcept.pass.cpp (100%)
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index f7d5575ebaffd..d34a47286cf58 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -33,14 +33,14 @@ template <class _Rp, class... _ArgTypes>
inline constexpr bool __is_function_ref<function_ref<_Rp, _ArgTypes...>> = true;
template <class _Fp, class _Tp>
-struct __function_ref_bind;
+struct __function_ref_bind {};
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...), _Tp> {
using type = _Rp(_ArgTypes...);
};
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
using type = _Rp(_ArgTypes...) noexcept;
};
@@ -48,7 +48,7 @@ struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
template <class _Tp, class _Mp, class _Gp>
requires is_object_v<_Mp>
struct __function_ref_bind<_Mp _Gp::*, _Tp> {
- using type = invoke_result_t<_Mp _Gp::*, _Tp&>;
+ using type = invoke_result_t<_Mp _Gp::*, _Tp&>();
};
template <class _Fp, class _Tp>
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 4e1cd13a02ae2..2e6ed45f32b19 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -46,6 +46,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
static constexpr bool __is_invocable_using = is_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
# endif
+ // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+ // pointers
union __storage_t {
void* __obj_ptr;
void const* __obj_const_ptr;
@@ -82,18 +84,17 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
}
}
- using __vtable_call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
- __vtable_call_t __vtable_call_;
+ using __call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+ __call_t __call_;
public:
template <class _Fp>
requires is_function_v<_Fp> && __is_invocable_using<_Fp>
_LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
: __storage_(__fn_ptr),
- __vtable_call_(
- [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
- return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
- }) {
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
+ }) {
_LIBCPP_ASSERT_UNCATEGORIZED(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
}
@@ -102,16 +103,15 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
__is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&>)
_LIBCPP_HIDE_FROM_ABI function_ref(_Fp&& __obj) noexcept
: __storage_(std::addressof(__obj)),
- __vtable_call_(
- [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
- _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
- return __obj(std::forward<_ArgTypes>(__args)...);
- }) {}
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+ return __obj(std::forward<_ArgTypes>(__args)...);
+ }) {}
template <auto _Fn>
requires __is_invocable_using<decltype(_Fn)>
constexpr function_ref(nontype_t<_Fn>) noexcept
- : __vtable_call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ : __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
}) {
if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
@@ -123,11 +123,10 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
: __storage_(std::addressof(__obj)),
- __vtable_call_(
- [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
- _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
- return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
- }) {
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+ return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+ }) {
if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
}
@@ -137,11 +136,10 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
: __storage_(__obj_ptr),
- __vtable_call_(
- [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
- auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
- return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
- }) {
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+ return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+ }) {
if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
}
@@ -160,17 +158,17 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
function_ref& operator=(_Tp) = delete;
constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
- return __vtable_call_(__storage_, std::forward<_ArgTypes>(__args)...);
+ return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
}
};
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
_Tp> {
using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
};
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
_Tp> {
using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
new file mode 100644
index 0000000000000..c7490fc0982e3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int fn(int, float) { return 42; }
+
+int fn_noexcept(int, float) noexcept { return 42; }
+
+struct S {
+ int data_mem = 42;
+
+ int fn_mem(int, float) { return 42; }
+ int fn_mem_noexcept(int, float) noexcept { return 42; }
+};
+
+void test() {
+ // template<class F>
+ // function_ref(F*) -> function_ref<F>;
+ {
+ std::function_ref fn_ref = fn;
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+ }
+ {
+ std::function_ref fn_ref = fn_noexcept;
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+ }
+
+ // template<auto f>
+ // function_ref(nontype_t<f>) -> function_ref<...>;
+ {
+ std::function_ref fn_ref = std::nontype_t<fn>();
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+ }
+ {
+ std::function_ref fn_ref = std::nontype_t<fn_noexcept>();
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+ }
+
+ // template<auto f, class T>
+ // function_ref(nontype_t<f>, T&&) -> function_ref<...>;
+ {
+ int arg = 0;
+ std::function_ref fn_ref = {std::nontype_t<fn>(), arg};
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+ }
+ {
+ S s;
+ std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&()>>);
+ }
+ {
+ const S s;
+ std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&()>>);
+ }
+ {
+ S s;
+ std::function_ref fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+ }
+ {
+ S s;
+ std::function_ref fn_ref = {std::nontype_t<&S::fn_mem_noexcept>(), s};
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+ }
+}
+
+int main(int, char**) {
+ test();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
>From 7fe10889b47e401aa18617620e8ff4c248816680 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 01:50:20 -0700
Subject: [PATCH 3/6] [libc++] P0792R14: 'function_ref'
---
libcxx/modules/std/functional.inc | 4 ++++
libcxx/modules/std/utility.inc | 5 +++++
2 files changed, 9 insertions(+)
diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index ddc7d023ee6dc..4d2be32f3f6bb 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -91,6 +91,10 @@ export namespace std {
using std::function;
+#if _LIBCPP_STD_VER >= 26
+ using std::function_ref;
+#endif // _LIBCPP_STD_VER >= 26
+
using std::swap;
using std::operator==;
diff --git a/libcxx/modules/std/utility.inc b/libcxx/modules/std/utility.inc
index 77c21b87640dd..28f8e36e30af9 100644
--- a/libcxx/modules/std/utility.inc
+++ b/libcxx/modules/std/utility.inc
@@ -89,6 +89,11 @@ export namespace std {
using std::in_place_index;
using std::in_place_index_t;
+#if _LIBCPP_STD_VER >= 26
+ using std::nontype;
+ using std::nontype_t;
+#endif // _LIBCPP_STD_VER >= 23
+
// [depr.relops]
namespace rel_ops {
using rel_ops::operator!=;
>From 285b767482c32dfc66dc56eb641ecb2bbe12dfe1 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 03:41:35 -0700
Subject: [PATCH 4/6] [libc++] P0792R14: 'function_ref'
---
libcxx/include/CMakeLists.txt | 2 +-
libcxx/include/__utility/nontype.h | 5 +++++
libcxx/include/module.modulemap | 2 ++
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 6dd28b79117af..27375cf2e9164 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -393,9 +393,9 @@ set(files
__functional/compose.h
__functional/default_searcher.h
__functional/function.h
+ __functional/function_ref.h
__functional/function_ref_common.h
__functional/function_ref_impl.h
- __functional/function_ref.h
__functional/hash.h
__functional/identity.h
__functional/invoke.h
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/nontype.h
index 86893f17e2d96..6a91d2cace162 100644
--- a/libcxx/include/__utility/nontype.h
+++ b/libcxx/include/__utility/nontype.h
@@ -27,6 +27,11 @@ struct nontype_t {
template <auto _Vp>
inline constexpr nontype_t<_Vp> nontype{};
+template <class>
+inline constexpr bool __is_nontype_t = false;
+template <auto _Vp>
+inline constexpr bool __is_nontype_t<nontype_t<_Vp>> = true;
+
#endif // _LIBCPP_STD_VER >= 26
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 5143f3fadfdc2..85ea6cdf35634 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1340,6 +1340,8 @@ module std_private_functional_compose [system] {
module std_private_functional_default_searcher [system] { header "__functional/default_searcher.h" }
module std_private_functional_function [system] { header "__functional/function.h" }
module std_private_functional_function_ref [system] { header "__functional/function_ref.h" }
+module std_private_functional_function_ref_common [system] { header "__functional/function_ref_common.h" }
+module std_private_functional_function_ref_impl [system] { textual header "__functional/function_ref_impl.h" }
module std_private_functional_hash [system] {
header "__functional/hash.h"
export std_cstdint
>From ee362ac8210b70cdb944ef78fe9e88e8548a7353 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 03:50:12 -0700
Subject: [PATCH 5/6] [libc++] P0792R14: 'function_ref'
---
.../include/__functional/function_ref_impl.h | 18 +++++++++---------
libcxx/include/__utility/nontype.h | 2 +-
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 2e6ed45f32b19..261beb2d04dad 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -53,10 +53,10 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
void const* __obj_const_ptr;
void (*__fn_ptr)();
- constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {};
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr){};
template <class _Tp>
- constexpr explicit __storage_t(_Tp* __ptr) noexcept {
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t(_Tp* __ptr) noexcept {
if constexpr (is_object_v<_Tp>) {
if constexpr (is_const_v<_Tp>) {
__obj_const_ptr = __ptr;
@@ -110,7 +110,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
template <auto _Fn>
requires __is_invocable_using<decltype(_Fn)>
- constexpr function_ref(nontype_t<_Fn>) noexcept
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>) noexcept
: __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
}) {
@@ -121,7 +121,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
- constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
: __storage_(std::addressof(__obj)),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
_LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
@@ -134,7 +134,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
template <auto _Fn, class _Tp>
requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
- constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
: __storage_(__obj_ptr),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
@@ -149,15 +149,15 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
}
}
- constexpr function_ref(const function_ref&) noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
- constexpr function_ref& operator=(const function_ref&) noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
template <class _Tp>
requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_nontype_t<_Tp>)
- function_ref& operator=(_Tp) = delete;
+ _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
- constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
+ _LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
}
};
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/nontype.h
index 6a91d2cace162..3a10840154009 100644
--- a/libcxx/include/__utility/nontype.h
+++ b/libcxx/include/__utility/nontype.h
@@ -21,7 +21,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <auto _Vp>
struct nontype_t {
- explicit nontype_t() = default;
+ _LIBCPP_HIDE_FROM_ABI explicit nontype_t() = default;
};
template <auto _Vp>
>From 379b00286259350f2a935869234fc0c654817fa8 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 03:55:54 -0700
Subject: [PATCH 6/6] [libc++] P0792R14: 'function_ref'
---
libcxx/include/__functional/function_ref_common.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index d34a47286cf58..b2d1864e582c5 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -11,6 +11,7 @@
#include <__config>
#include <__type_traits/invoke.h>
+#include <__type_traits/is_function.h>
#include <__type_traits/is_object.h>
#include <__type_traits/remove_pointer.h>
#include <__utility/nontype.h>
More information about the libcxx-commits
mailing list