Skip to content

[libc++] P0792R14: std::function_ref #94894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +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/hash.h
__functional/identity.h
__functional/invoke.h
Expand Down Expand Up @@ -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
Expand Down
47 changes: 47 additions & 0 deletions libcxx/include/__functional/function_ref.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//===----------------------------------------------------------------------===//
//
// 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>
#include <__functional/function_ref_common.h>

#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
73 changes: 73 additions & 0 deletions libcxx/include/__functional/function_ref_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//===----------------------------------------------------------------------===//
//
// 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_function.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
179 changes: 179 additions & 0 deletions libcxx/include/__functional/function_ref_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
//===----------------------------------------------------------------------===//
//
// 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/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_reference.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

// 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;
void (*__fn_ptr)();

_LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {}

template <class _Tp>
_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;
} 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 __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),
__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)),
__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)>
_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)...);
}) {
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&>
_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);
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*>
_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);
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");
}
}

_LIBCPP_HIDE_FROM_ABI constexpr function_ref(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>)
_LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;

_LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
return __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
39 changes: 39 additions & 0 deletions libcxx/include/__utility/nontype.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//===----------------------------------------------------------------------===//
//
// 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 {
_LIBCPP_HIDE_FROM_ABI explicit nontype_t() = default;
};

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

#endif // _LIBCPP___UTILITY_NONTYPE_H
6 changes: 6 additions & 0 deletions libcxx/include/functional
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,11 @@ template <class R, class ... ArgTypes>
template <class R, class ... ArgTypes>
void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;

// [func.wrap.ref], non-owning wrapper
template<class... S> class function_ref; // freestanding, not defined, since C++26
template<class R, class... ArgTypes>
class function_ref<R(ArgTypes...) cv noexcept(noex)>; // freestanding, since C++26

template <class T> struct hash;

template <> struct hash<bool>;
Expand Down Expand Up @@ -541,6 +546,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>
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,9 @@ 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] { textual 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
Expand Down Expand Up @@ -2073,6 +2076,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
Expand Down
Loading