Skip to content

Commit 352fd1b

Browse files
committed
[libc++] Implement P0288R9 (move_only_function)
1 parent 915e9ad commit 352fd1b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2836
-51
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ Status
356356
---------------------------------------------------------- -----------------
357357
``__cpp_lib_modules`` ``202207L``
358358
---------------------------------------------------------- -----------------
359-
``__cpp_lib_move_only_function`` *unimplemented*
359+
``__cpp_lib_move_only_function`` ``202110L``
360360
---------------------------------------------------------- -----------------
361361
``__cpp_lib_optional`` ``202110L``
362362
---------------------------------------------------------- -----------------

libcxx/docs/ReleaseNotes/22.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ Implemented Papers
4949
- P2835R7: Expose ``std::atomic_ref``'s object address (`Github <https://llvm.org/PR118377>`__)
5050
- P2944R3: Comparisons for ``reference_wrapper`` (`Github <https://llvm.org/PR105424>`__)
5151
- P3168R2: Give ``std::optional`` Range Support (`Github <https://llvm.org/PR105430>`__)
52+
- P0288R9 - ``move_only_function`` (`Github <https://llvm.org/PR105157>`__) This feature is currently experimental and
53+
therefore requires ``-fexperimental-library``.
5254

5355
Improvements and New Features
5456
-----------------------------

libcxx/docs/Status/Cxx23Papers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"`P2136R3 <https://wg21.link/P2136R3>`__","invoke_r","2021-06 (Virtual)","|Complete|","17","`#105155 <https://github.com/llvm/llvm-project/issues/105155>`__",""
2424
"`P2166R1 <https://wg21.link/P2166R1>`__","A Proposal to Prohibit std::basic_string and std::basic_string_view construction from nullptr","2021-06 (Virtual)","|Complete|","13","`#105156 <https://github.com/llvm/llvm-project/issues/105156>`__",""
2525
"","","","","","",""
26-
"`P0288R9 <https://wg21.link/P0288R9>`__","``any_invocable``","2021-10 (Virtual)","","","`#105157 <https://github.com/llvm/llvm-project/issues/105157>`__",""
26+
"`P0288R9 <https://wg21.link/P0288R9>`__","``move_only_function``","2021-10 (Virtual)","|Complete|","22","`#105157 <https://github.com/llvm/llvm-project/issues/105157>`__",""
2727
"`P0798R8 <https://wg21.link/P0798R8>`__","Monadic operations for ``std::optional``","2021-10 (Virtual)","|Complete|","14","`#105158 <https://github.com/llvm/llvm-project/issues/105158>`__",""
2828
"`P0849R8 <https://wg21.link/P0849R8>`__","``auto(x)``: ``DECAY_COPY`` in the language","2021-10 (Virtual)","|Complete|","14","`#105159 <https://github.com/llvm/llvm-project/issues/105159>`__",""
2929
"`P1072R10 <https://wg21.link/P1072R10>`__","``basic_string::resize_and_overwrite``","2021-10 (Virtual)","|Complete|","14","`#105160 <https://github.com/llvm/llvm-project/issues/105160>`__",""

libcxx/include/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,9 @@ set(files
430430
__functional/is_transparent.h
431431
__functional/mem_fn.h
432432
__functional/mem_fun_ref.h
433+
__functional/move_only_function.h
434+
__functional/move_only_function_common.h
435+
__functional/move_only_function_impl.h
433436
__functional/not_fn.h
434437
__functional/operations.h
435438
__functional/perfect_forward.h

libcxx/include/__configuration/experimental.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@
3333
#define _LIBCPP_HAS_EXPERIMENTAL_TZDB _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
3434
#define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
3535
#define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
36+
#define _LIBCPP_HAS_EXPERIMENTAL_MOVE_ONLY_FUNCTION _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
3637

3738
#endif // _LIBCPP___CONFIGURATION_EXPERIMENTAL_H
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H
10+
#define _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H
11+
12+
#include <__config>
13+
14+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
15+
# pragma GCC system_header
16+
#endif
17+
18+
#if _LIBCPP_STD_VER >= 23 && !defined(_LIBCPP_COMPILER_GCC) && _LIBCPP_HAS_EXPERIMENTAL_MOVE_ONLY_FUNCTION
19+
20+
// move_only_function design:
21+
//
22+
// move_only_function has a small buffer with a size of `3 * sizeof(void*)` bytes. This buffer can only be used when the
23+
// object to be stored is "trivially relocatable" (currently only when it is trivially move constructible and trivially
24+
// destructible). The vtable entry for the destructor is a null pointer when the stored object is trivially
25+
// destructible.
26+
//
27+
// trivially relocatable: It would also be possible to store nothrow_move_constructible types, but that would mean
28+
// that move_only_function itself would not be trivially relocatable anymore. The decision to keep move_only_function
29+
// trivially relocatable was made because we expect move_only_function to be stored persistently most of the time, since
30+
// std::function_ref can be used for cases where a function object doesn't need to be stored.
31+
//
32+
// buffer size: We did a survey of six implementations from various vendors. Three of them had a buffer size of 24 bytes
33+
// on 64 bit systems. This will also allow storing a function object containing a std::string or std::vector inside the
34+
// small buffer once there is a language definition of "trivially relocatable".
35+
//
36+
// interaction with copyable_function: When converting a copyable_function into a move_only_function we want to avoid
37+
// wrapping the copyable_function inside the move_only_function to avoid a double indirection. Instead, we copy the
38+
// small buffer and use copyable_function's vtable.
39+
40+
// NOLINTBEGIN(readability-duplicate-include)
41+
# define _LIBCPP_IN_MOVE_ONLY_FUNCTION_H
42+
43+
# include <__functional/move_only_function_impl.h>
44+
45+
# define _LIBCPP_MOVE_ONLY_FUNCTION_REF &
46+
# include <__functional/move_only_function_impl.h>
47+
48+
# define _LIBCPP_MOVE_ONLY_FUNCTION_REF &&
49+
# include <__functional/move_only_function_impl.h>
50+
51+
# define _LIBCPP_MOVE_ONLY_FUNCTION_CV const
52+
# include <__functional/move_only_function_impl.h>
53+
54+
# define _LIBCPP_MOVE_ONLY_FUNCTION_CV const
55+
# define _LIBCPP_MOVE_ONLY_FUNCTION_REF &
56+
# include <__functional/move_only_function_impl.h>
57+
58+
# define _LIBCPP_MOVE_ONLY_FUNCTION_CV const
59+
# define _LIBCPP_MOVE_ONLY_FUNCTION_REF &&
60+
# include <__functional/move_only_function_impl.h>
61+
62+
# undef _LIBCPP_IN_MOVE_ONLY_FUNCTION_H
63+
// NOLINTEND(readability-duplicate-include)
64+
65+
#endif // _LIBCPP_STD_VER >= 23 && !defined(_LIBCPP_COMPILER_GCC) && _LIBCPP_HAS_EXPERIMENTAL_MOVE_ONLY_FUNCTION
66+
67+
#endif // _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_COMMON_H
10+
#define _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_COMMON_H
11+
12+
#include <__config>
13+
14+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
15+
# pragma GCC system_header
16+
#endif
17+
18+
#if _LIBCPP_STD_VER >= 23 && _LIBCPP_HAS_EXPERIMENTAL_MOVE_ONLY_FUNCTION
19+
20+
_LIBCPP_BEGIN_NAMESPACE_STD
21+
22+
template <class...>
23+
class move_only_function;
24+
25+
template <class>
26+
inline constexpr bool __is_move_only_function_v = false;
27+
28+
template <class... _Args>
29+
inline constexpr bool __is_move_only_function_v<move_only_function<_Args...>> = true;
30+
31+
template <class _BufferT, class _ReturnT, class... _ArgTypes>
32+
struct _MoveOnlyFunctionVTable {
33+
using _CallFunc _LIBCPP_NODEBUG = _ReturnT(_BufferT&, _ArgTypes...);
34+
using _DestroyFunc _LIBCPP_NODEBUG = void(_BufferT&) noexcept;
35+
36+
_CallFunc* __call_;
37+
_DestroyFunc* __destroy_;
38+
};
39+
40+
_LIBCPP_END_NAMESPACE_STD
41+
42+
#endif // _LIBCPP_STD_VER >= 23 && _LIBCPP_HAS_EXPERIMENTAL_MOVE_ONLY_FUNCTION
43+
44+
#endif // _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_COMMON_H
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// This header is unguarded on purpose. This header is an implementation detail of move_only_function.h
10+
// and generates multiple versions of std::move_only_function
11+
12+
#include <__assert>
13+
#include <__config>
14+
#include <__cstddef/nullptr_t.h>
15+
#include <__cstddef/size_t.h>
16+
#include <__functional/invoke.h>
17+
#include <__functional/move_only_function_common.h>
18+
#include <__memory/addressof.h>
19+
#include <__memory/construct_at.h>
20+
#include <__type_traits/decay.h>
21+
#include <__type_traits/invoke.h>
22+
#include <__type_traits/is_constructible.h>
23+
#include <__type_traits/is_function.h>
24+
#include <__type_traits/is_member_pointer.h>
25+
#include <__type_traits/is_pointer.h>
26+
#include <__type_traits/is_same.h>
27+
#include <__type_traits/is_trivially_destructible.h>
28+
#include <__type_traits/remove_cvref.h>
29+
#include <__type_traits/remove_pointer.h>
30+
#include <__utility/exchange.h>
31+
#include <__utility/forward.h>
32+
#include <__utility/in_place.h>
33+
#include <__utility/move.h>
34+
#include <__utility/small_buffer.h>
35+
#include <__utility/swap.h>
36+
#include <initializer_list>
37+
38+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
39+
# pragma GCC system_header
40+
#endif
41+
42+
#ifndef _LIBCPP_IN_MOVE_ONLY_FUNCTION_H
43+
# error This header should only be included from move_only_function.h
44+
#endif
45+
46+
#ifndef _LIBCPP_MOVE_ONLY_FUNCTION_CV
47+
# define _LIBCPP_MOVE_ONLY_FUNCTION_CV
48+
#endif
49+
50+
#ifndef _LIBCPP_MOVE_ONLY_FUNCTION_REF
51+
# define _LIBCPP_MOVE_ONLY_FUNCTION_REF
52+
# define _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS _LIBCPP_MOVE_ONLY_FUNCTION_CV&
53+
#else
54+
# define _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS _LIBCPP_MOVE_ONLY_FUNCTION_CV _LIBCPP_MOVE_ONLY_FUNCTION_REF
55+
#endif
56+
57+
#define _LIBCPP_MOVE_ONLY_FUNCTION_CVREF _LIBCPP_MOVE_ONLY_FUNCTION_CV _LIBCPP_MOVE_ONLY_FUNCTION_REF
58+
59+
_LIBCPP_PUSH_MACROS
60+
#include <__undef_macros>
61+
62+
_LIBCPP_BEGIN_NAMESPACE_STD
63+
64+
template <class...>
65+
class move_only_function;
66+
67+
template <class _ReturnT, class... _ArgTypes, bool __is_noexcept>
68+
class [[_Clang::__trivial_abi__]]
69+
move_only_function<_ReturnT(_ArgTypes...) _LIBCPP_MOVE_ONLY_FUNCTION_CVREF noexcept(__is_noexcept)> {
70+
private:
71+
static constexpr size_t __buffer_size_ = 3 * sizeof(void*);
72+
static constexpr size_t __buffer_alignment_ = alignof(void*);
73+
using _BufferT _LIBCPP_NODEBUG = __small_buffer<__buffer_size_, __buffer_alignment_>;
74+
75+
using _VTable _LIBCPP_NODEBUG = _MoveOnlyFunctionVTable<_BufferT, _ReturnT, _ArgTypes...>;
76+
77+
template <class _Functor>
78+
static constexpr _VTable __vtable_var_ = {
79+
.__call_ = [](_BufferT& __buffer, _ArgTypes... __args) noexcept(__is_noexcept) -> _ReturnT {
80+
return std::invoke_r<_ReturnT>(
81+
static_cast<_Functor _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS>(*__buffer.__get<_Functor>()),
82+
std::forward<_ArgTypes>(__args)...);
83+
},
84+
.__destroy_ = (_BufferT::__fits_in_buffer<_Functor> && is_trivially_destructible_v<_Functor>)
85+
? nullptr
86+
: [](_BufferT& __buffer) noexcept -> void {
87+
std::destroy_at(__buffer.__get<_Functor>());
88+
__buffer.__dealloc<_Functor>();
89+
}};
90+
91+
template <class _VT>
92+
static constexpr bool __is_callable_from = [] {
93+
using _DVT = decay_t<_VT>;
94+
if constexpr (__is_noexcept) {
95+
return is_nothrow_invocable_r_v<_ReturnT, _DVT _LIBCPP_MOVE_ONLY_FUNCTION_CVREF, _ArgTypes...> &&
96+
is_nothrow_invocable_r_v<_ReturnT, _DVT _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS, _ArgTypes...>;
97+
} else {
98+
return is_invocable_r_v<_ReturnT, _DVT _LIBCPP_MOVE_ONLY_FUNCTION_CVREF, _ArgTypes...> &&
99+
is_invocable_r_v<_ReturnT, _DVT _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS, _ArgTypes...>;
100+
}
101+
}();
102+
103+
template <class _Func, class... _Args>
104+
_LIBCPP_HIDE_FROM_ABI void __construct(_Args&&... __args) {
105+
static_assert(is_constructible_v<decay_t<_Func>, _Func>);
106+
107+
using _StoredFunc = decay_t<_Func>;
108+
__vtable_ = std::addressof(__vtable_var_<_StoredFunc>);
109+
__buffer_.__construct<_StoredFunc>(std::forward<_Args>(__args)...);
110+
}
111+
112+
_LIBCPP_HIDE_FROM_ABI void __reset() noexcept {
113+
if (__vtable_ && __vtable_->__destroy_)
114+
__vtable_->__destroy_(__buffer_);
115+
__vtable_ = nullptr;
116+
}
117+
118+
public:
119+
using result_type = _ReturnT;
120+
121+
// [func.wrap.move.ctor]
122+
move_only_function() noexcept = default;
123+
_LIBCPP_HIDE_FROM_ABI move_only_function(nullptr_t) noexcept {}
124+
_LIBCPP_HIDE_FROM_ABI move_only_function(move_only_function&& __other) noexcept
125+
: __vtable_(__other.__vtable_), __buffer_(std::move(__other.__buffer_)) {
126+
__other.__vtable_ = nullptr;
127+
}
128+
129+
template <class _Func>
130+
requires(!is_same_v<remove_cvref_t<_Func>, move_only_function> && !__is_inplace_type<_Func>::value &&
131+
__is_callable_from<_Func>)
132+
_LIBCPP_HIDE_FROM_ABI move_only_function(_Func&& __func) noexcept {
133+
using _StoredFunc = decay_t<_Func>;
134+
135+
if constexpr ((is_pointer_v<_StoredFunc> && is_function_v<remove_pointer_t<_StoredFunc>>) ||
136+
is_member_function_pointer_v<_StoredFunc>) {
137+
if (__func != nullptr) {
138+
__vtable_ = std::addressof(__vtable_var_<_StoredFunc>);
139+
static_assert(_BufferT::__fits_in_buffer<_StoredFunc>);
140+
__buffer_.__construct<_StoredFunc>(std::forward<_Func>(__func));
141+
}
142+
} else if constexpr (__is_move_only_function_v<_StoredFunc>) {
143+
if (__func) {
144+
__vtable_ = std::exchange(__func.__vtable_, nullptr);
145+
__buffer_ = std::move(__func.__buffer_);
146+
}
147+
} else {
148+
__construct<_Func>(std::forward<_Func>(__func));
149+
}
150+
}
151+
152+
template <class _Func, class... _Args>
153+
requires is_constructible_v<decay_t<_Func>, _Args...> && __is_callable_from<_Func>
154+
_LIBCPP_HIDE_FROM_ABI explicit move_only_function(in_place_type_t<_Func>, _Args&&... __args) {
155+
static_assert(is_same_v<decay_t<_Func>, _Func>);
156+
__construct<_Func>(std::forward<_Args>(__args)...);
157+
}
158+
159+
template <class _Func, class _InitListType, class... _Args>
160+
requires is_constructible_v<decay_t<_Func>, initializer_list<_InitListType>&, _Args...> && __is_callable_from<_Func>
161+
_LIBCPP_HIDE_FROM_ABI explicit move_only_function(
162+
in_place_type_t<_Func>, initializer_list<_InitListType> __il, _Args&&... __args) {
163+
static_assert(is_same_v<decay_t<_Func>, _Func>);
164+
__construct<_Func>(__il, std::forward<_Args>(__args)...);
165+
}
166+
167+
_LIBCPP_HIDE_FROM_ABI move_only_function& operator=(move_only_function&& __other) noexcept {
168+
move_only_function(std::move(__other)).swap(*this);
169+
return *this;
170+
}
171+
172+
_LIBCPP_HIDE_FROM_ABI move_only_function& operator=(nullptr_t) noexcept {
173+
__reset();
174+
return *this;
175+
}
176+
177+
template <class _Func>
178+
requires(!is_same_v<remove_cvref_t<_Func>, move_only_function> && !__is_inplace_type<_Func>::value &&
179+
__is_callable_from<_Func>)
180+
_LIBCPP_HIDE_FROM_ABI move_only_function& operator=(_Func&& __func) {
181+
move_only_function(std::forward<_Func>(__func)).swap(*this);
182+
return *this;
183+
}
184+
185+
_LIBCPP_HIDE_FROM_ABI ~move_only_function() { __reset(); }
186+
187+
// [func.wrap.move.inv]
188+
_LIBCPP_HIDE_FROM_ABI explicit operator bool() const noexcept { return __vtable_; }
189+
190+
_LIBCPP_HIDE_FROM_ABI _ReturnT operator()(_ArgTypes... __args) _LIBCPP_MOVE_ONLY_FUNCTION_CVREF
191+
noexcept(__is_noexcept) {
192+
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
193+
const auto __call = static_cast<_ReturnT (*)(_BufferT&, _ArgTypes...)>(__vtable_->__call_);
194+
return __call(__buffer_, std::forward<_ArgTypes>(__args)...);
195+
}
196+
197+
// [func.wrap.move.util]
198+
_LIBCPP_HIDE_FROM_ABI void swap(move_only_function& __other) noexcept {
199+
std::swap(__vtable_, __other.__vtable_);
200+
std::swap(__buffer_, __other.__buffer_);
201+
}
202+
203+
_LIBCPP_HIDE_FROM_ABI friend void swap(move_only_function& __lhs, move_only_function& __rhs) noexcept {
204+
__lhs.swap(__rhs);
205+
}
206+
207+
_LIBCPP_HIDE_FROM_ABI friend bool operator==(const move_only_function& __func, nullptr_t) noexcept { return !__func; }
208+
209+
private:
210+
const _VTable* __vtable_ = nullptr;
211+
mutable _BufferT __buffer_;
212+
213+
template <class...>
214+
friend class move_only_function;
215+
};
216+
217+
#undef _LIBCPP_MOVE_ONLY_FUNCTION_CV
218+
#undef _LIBCPP_MOVE_ONLY_FUNCTION_REF
219+
#undef _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS
220+
#undef _LIBCPP_MOVE_ONLY_FUNCTION_CVREF
221+
222+
_LIBCPP_END_NAMESPACE_STD
223+
224+
_LIBCPP_POP_MACROS

0 commit comments

Comments
 (0)