[llvm-branch-commits] [libcxx] [libc++] Implement std::move_only_function (P0288R9) (PR #94670)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Jun 6 20:45:29 PDT 2024
================
@@ -0,0 +1,233 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of move_only_function.h
+// and generates multiple versions of std::move_only_function
+
+#include <__config>
+#include <__functional/invoke.h>
+#include <__functional/move_only_function_common.h>
+#include <__type_traits/is_trivially_destructible.h>
+#include <__utility/exchange.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <__utility/pointer_int_pair.h>
+#include <__utility/small_buffer.h>
+#include <__utility/swap.h>
+#include <cstddef>
+#include <cstring>
+#include <initializer_list>
+#include <new>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP_IN_MOVE_ONLY_FUNCTION_H
+# error This header should only be included from move_only_function.h
+#endif
+
+#ifndef _LIBCPP_MOVE_ONLY_FUNCTION_CV
+# define _LIBCPP_MOVE_ONLY_FUNCTION_CV
+#endif
+
+#ifndef _LIBCPP_MOVE_ONLY_FUNCTION_REF
----------------
EricWF wrote:
Here's what I'm imagining...
```c++
#ifndef _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H
#define _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H
#include <__assert>
#include <__config>
#include <__functional/invoke.h>
#include <__type_traits/decay.h>
#include <__type_traits/invoke.h>
#include <__type_traits/is_function.h>
#include <__type_traits/is_member_function_pointer.h>
#include <__type_traits/is_pointer.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_constructible.h>
#include <__type_traits/is_trivially_destructible.h>
#include <__type_traits/remove_cvref.h>
#include <__type_traits/remove_pointer.h>
#include <__utility/forward.h>
#include <__utility/in_place.h>
#include <__utility/swap.h>
#include <cstddef>
#include <initializer_list>
#include <new>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
enum class _Quals : unsigned {
_None = 0,
_Const = 1,
_RVRef = 2,
_LVRef = 4,
_NoExcept = 8,
};
constexpr _Quals operator|(_Quals __x, _Quals __y) {
return static_cast<_Quals>(static_cast<unsigned>(__x) | static_cast<unsigned>(__y));
}
constexpr _Quals operator&(_Quals __x, _Quals __y) {
return static_cast<_Quals>(static_cast<unsigned>(__x) & static_cast<unsigned>(__y));
}
constexpr _Quals operator^(_Quals __x, _Quals __y) {
return static_cast<_Quals>(static_cast<unsigned>(__x) ^ static_cast<unsigned>(__y));
}
constexpr _Quals operator~(_Quals __x) { return static_cast<_Quals>(~static_cast<unsigned>(__x)); }
constexpr bool operator==(const _Quals __x, const _Quals __y) {
return static_cast<unsigned>(__x) == static_cast<unsigned>(__y);
}
constexpr bool operator!=(const _Quals __x, const _Quals __y) {
return static_cast<unsigned>(__x) != static_cast<unsigned>(__y);
}
constexpr _Quals __cvref_quals(_Quals __q) { return __q & (_Quals::_Const | _Quals::_RVRef | _Quals::_LVRef); }
constexpr bool __is_noexcept(_Quals __q) { return (__q & _Quals::_NoExcept) == _Quals::_NoExcept; }
constexpr _Quals __ref_quals(_Quals __q) { return __q & (_Quals::_RVRef | _Quals::_LVRef); }
template <class _Ptr>
struct __unqualify_type;
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...)> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_None;
};
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_Const;
};
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...)&> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_None | _Quals::_LVRef;
};
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const&> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_Const | _Quals::_LVRef;
};
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) &&> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_None | _Quals::_RVRef;
};
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const&&> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_Const | _Quals::_RVRef;
};
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) noexcept> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_None | _Quals::_NoExcept;
};
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const noexcept> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_Const | _Quals::_NoExcept;
};
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) & noexcept> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_None | _Quals::_LVRef | _Quals::_NoExcept;
};
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const & noexcept> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_Const | _Quals::_LVRef | _Quals::_NoExcept;
};
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) && noexcept> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_None | _Quals::_RVRef | _Quals::_NoExcept;
};
template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const && noexcept> {
using type = _Ret(_Args...);
static constexpr _Quals __qual = _Quals::_Const | _Quals::_RVRef | _Quals::_NoExcept;
};
template <class _Tp>
struct __identity {
using type = _Tp;
};
template <_Quals __qual, class _Ret, class... _Args>
constexpr auto __qualify_function_type() {
if constexpr (__qual == _Quals::_None) {
return __identity<_Ret(_Args...)>{};
} else if constexpr (__qual == _Quals::_Const) {
return __identity<_Ret(_Args...) const>{};
} else if constexpr (__qual == _Quals::_LVRef) {
return __identity<_Ret(_Args...)&>{};
} else if constexpr (__qual == (_Quals::_Const | _Quals::_LVRef)) {
return __identity<_Ret(_Args...) const&>{};
} else if constexpr (__qual == _Quals::_RVRef) {
return __identity<_Ret(_Args...) &&>{};
} else if constexpr (__qual == (_Quals::_Const | _Quals::_RVRef)) {
return __identity<_Ret(_Args...) const&&>{};
} else if constexpr (__qual == _Quals::_NoExcept) {
return __identity<_Ret(_Args...) noexcept>{};
} else if constexpr (__qual == (_Quals::_Const | _Quals::_NoExcept)) {
return __identity<_Ret(_Args...) const noexcept>{};
} else if constexpr (__qual == (_Quals::_LVRef | _Quals::_NoExcept)) {
return __identity<_Ret(_Args...) & noexcept>{};
} else if constexpr (__qual == (_Quals::_Const | _Quals::_LVRef | _Quals::_NoExcept)) {
return __identity<_Ret(_Args...) const & noexcept>{};
} else if constexpr (__qual == (_Quals::_RVRef | _Quals::_NoExcept)) {
return __identity < _Ret(_Args...) && noexcept > {};
} else if constexpr (__qual == (_Quals::_Const | _Quals::_RVRef | _Quals::_NoExcept)) {
return __identity < _Ret(_Args...) const && noexcept > {};
} else {
static_assert(sizeof(__qual) == 0, "Invalid qualifier");
}
}
template <_Quals __qual, class _Ret>
constexpr auto __qualify_type_impl() {
static_assert(!__is_noexcept(__qual), "Noexcept qualifier is not allowed on type");
if constexpr (__qual == _Quals::_None) {
return __identity<_Ret>{};
} else if constexpr (__qual == _Quals::_Const) {
return __identity<_Ret const>{};
} else if constexpr (__qual == _Quals::_LVRef) {
return __identity<_Ret&>{};
} else if constexpr (__qual == (_Quals::_Const | _Quals::_LVRef)) {
return __identity<_Ret const&>{};
} else if constexpr (__qual == _Quals::_RVRef) {
return __identity<_Ret&&>{};
} else if constexpr (__qual == (_Quals::_Const | _Quals::_RVRef)) {
return __identity<_Ret const&&>{};
} else {
static_assert(sizeof(__qual) == 0, "Invalid qualifier");
}
}
template <_Quals __qual, class _Ret>
using __qualify_type_t = typename decltype(__qualify_type_impl<__qual, _Ret>())::type;
template <_Quals __quals, class _Tp>
__qualify_type_t<__quals, _Tp> __cast_as(void* __ptr) {
return std::forward<__qualify_type_t<__quals, _Tp>>(*static_cast<_Tp*>(__ptr));
}
template <class _Tp, _Quals __q>
struct __move_only_function_base;
struct __move_only_function_buffer {
union {
void* __ptr_;
std::byte __buf_[3 * sizeof(void*)];
};
};
struct _AllocGuard {
_AllocGuard() = delete;
_AllocGuard(_AllocGuard const&) = delete;
_AllocGuard(size_t __size, size_t __align)
: __ptr_(__libcpp_allocate(__size, __align)), __size_(__size), __align_(__align) {}
void* __release() {
void* __ret = __ptr_;
__ptr_ = nullptr;
return __ret;
}
~_AllocGuard() {
if (__ptr_) {
__libcpp_deallocate(__ptr_, __size_, __align_);
}
}
void* __ptr_;
size_t __size_;
size_t __align_;
};
template <class _Functor>
constexpr bool __use_inline_storage =
sizeof(_Functor) <= sizeof(__move_only_function_buffer) &&
alignof(_Functor) <= alignof(__move_only_function_buffer) && std::is_trivially_destructible_v<_Functor> &&
std::is_trivially_move_constructible_v<_Functor>;
template <class...>
struct move_only_function;
template <class _Ret, class... _Args, _Quals __q>
struct __move_only_function_base<_Ret(_Args...), __q> {
private:
using _BufferT = __move_only_function_buffer;
static constexpr _Quals __inv_quals =
__ref_quals(__q) == _Quals::_None ? __cvref_quals(__q) | _Quals::_LVRef : __cvref_quals(__q);
struct _VTable {
using __destroy_t = void (*)(_BufferT&);
using __call_t = _Ret (*)(_BufferT&, _Args...) noexcept(__is_noexcept(__q));
__call_t __call_ = nullptr;
__destroy_t __destroy_ = nullptr;
template <class _RawFunctor>
static constexpr _VTable __create() {
using _Functor = std::decay_t<_RawFunctor>;
if constexpr (__use_inline_storage<_Functor>) {
return _VTable{.__call_ = [](_BufferT& __buffer, _Args... __args) noexcept(__is_noexcept(__q)) -> _Ret {
return std::invoke_r<_Ret>(
__cast_as<__inv_quals, _Functor>(__buffer.__buf_), std::forward<_Args>(__args)...);
},
.__destroy_ = nullptr};
} else {
return _VTable{.__call_ = [](_BufferT& __buffer, _Args... __args) noexcept(__is_noexcept(__q)) -> _Ret {
return std::invoke_r<_Ret>(
__cast_as<__inv_quals, _Functor>(__buffer.__ptr_), std::forward<_Args>(__args)...);
},
.__destroy_ = [](_BufferT& __buffer) noexcept -> void {
static_cast<_Functor*>(__buffer.__ptr_)->~_Functor();
__libcpp_deallocate(__buffer.__ptr_, sizeof(_Functor), alignof(_Functor));
}};
};
}
};
template <class _Functor>
static constexpr _VTable __functor_vtable = _VTable::template __create<_Functor>();
mutable _BufferT __buffer_;
const _VTable* __vtable_ = {};
template <class _VT>
static constexpr bool __is_callable_from = []() {
using _DVT = decay_t<_VT>;
if constexpr (__is_noexcept(__q)) {
return is_nothrow_invocable_r_v<_Ret, __qualify_type_t<__cvref_quals(__q), _DVT>, _Args...> &&
is_nothrow_invocable_r_v<_Ret, __qualify_type_t<__inv_quals, _DVT>, _Args...>;
} else {
return is_invocable_r_v<_Ret, __qualify_type_t<__cvref_quals(__q), _DVT>, _Args...> &&
is_invocable_r_v<_Ret, __qualify_type_t<__inv_quals, _DVT>, _Args...>;
}
}();
template <class _Func, class... _FArgs>
void __construct(_FArgs&&... __args) {
using _DFunc = decay_t<_Func>;
if (__use_inline_storage<_DFunc>) {
::new (&__buffer_.__buf_) _DFunc(std::forward<_FArgs>(__args)...);
} else {
_AllocGuard __alloc(sizeof(_DFunc), alignof(_DFunc));
::new (__alloc.__ptr_) _DFunc(std::forward<_FArgs>(__args)...);
__buffer_.__ptr_ = __alloc.__release();
}
__vtable_ = &__functor_vtable<_Func>;
}
_LIBCPP_HIDE_FROM_ABI void __reset() {
if (__vtable_ && __vtable_->__destroy_)
__vtable_->__destroy_(__buffer_);
__vtable_ = nullptr;
}
public:
using result_type = _Ret;
// [func.wrap.move.ctor]
__move_only_function_base() noexcept = default;
_LIBCPP_HIDE_FROM_ABI __move_only_function_base(__move_only_function_base&& __other) noexcept
: __vtable_(__other.__vtable_), __buffer_(__other.__buffer_) {
__other.__vtable_ = {};
}
template <_Quals __call_qual>
static constexpr bool __has_cvref_quals = (__cvref_quals(__q) & __call_qual) == __call_qual;
_LIBCPP_HIDE_FROM_ABI ~__move_only_function_base() { __reset(); }
_LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) noexcept(__is_noexcept(__q))
requires __has_cvref_quals<_Quals::_None>
{
_LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
}
_LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) const noexcept(__is_noexcept(__q))
requires __has_cvref_quals<_Quals::_Const>
{
_LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
}
_LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) & noexcept(__is_noexcept(__q))
requires __has_cvref_quals< _Quals::_LVRef>
{
_LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
}
_LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) && noexcept(__is_noexcept(__q))
requires __has_cvref_quals<_Quals::_RVRef>
{
_LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
}
_LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) const& noexcept(__is_noexcept(__q))
requires __has_cvref_quals<_Quals::_Const | _Quals::_LVRef>
{
_LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
}
_LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) const&& noexcept(__is_noexcept(__q))
requires __has_cvref_quals<_Quals::_Const | _Quals::_RVRef>
{
_LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
}
private:
template <class, _Quals>
friend struct __move_only_function_base;
template <class...>
friend struct move_only_function;
};
template <class _Ret>
struct __is_move_only_function : false_type {};
template <class... _Obj>
struct __is_move_only_function<move_only_function<_Obj...>> : true_type{};
template <class _Tp>
struct move_only_function<_Tp>
: __move_only_function_base<typename __unqualify_type<_Tp>::type, __unqualify_type<_Tp>::__qual> {
using __base = __move_only_function_base<typename __unqualify_type<_Tp>::type, __unqualify_type<_Tp>::__qual>;
move_only_function() noexcept = default;
_LIBCPP_HIDE_FROM_ABI move_only_function(nullptr_t) noexcept {}
move_only_function(move_only_function const&) = delete;
move_only_function(move_only_function&&) = default;
~move_only_function() = default;
template <class _Func>
requires(!is_same_v<remove_cvref_t<_Func>, move_only_function> && !__is_inplace_type<_Func>::value &&
__base::template __is_callable_from<_Func>)
_LIBCPP_HIDE_FROM_ABI move_only_function(_Func&& __func) {
using _StoredFunc = decay_t<_Func>;
if constexpr ((is_pointer_v<_StoredFunc> && is_function_v<remove_pointer_t<_StoredFunc>>) ||
is_member_function_pointer_v<_StoredFunc>) {
if (__func != nullptr) {
this->template __construct<_StoredFunc>(std::forward<_Func>(__func));
}
} else if constexpr (__is_move_only_function<_StoredFunc>::value) {
if (__func) {
std::swap(this->__vtable_, __func.__vtable_);
this->__buffer_ = __func.__buffer_;
}
} else {
this->template __construct<_Func>(std::forward<_Func>(__func));
}
}
template <class _Func, class... _FArgs>
requires is_constructible_v<decay_t<_Func>, _FArgs...> && __base::template
__is_callable_from<_Func> _LIBCPP_HIDE_FROM_ABI explicit move_only_function(
in_place_type_t<_Func>, _FArgs&&... __args) {
static_assert(is_same_v<decay_t<_Func>, _Func>);
this->template __construct<_Func>(std::forward<_FArgs>(__args)...);
}
template <class _Func, class _InitListType, class... _Args>
requires is_constructible_v<decay_t<_Func>, initializer_list<_InitListType>&, _Args...> && __base::template
__is_callable_from<_Func> _LIBCPP_HIDE_FROM_ABI explicit move_only_function(
in_place_type_t<_Func>, initializer_list<_InitListType> __il, _Args&&... __args) {
static_assert(is_same_v<decay_t<_Func>, _Func>);
this->template __construct<_Func>(__il, std::forward<_Args>(__args)...);
}
_LIBCPP_HIDE_FROM_ABI move_only_function& operator=(move_only_function&& __other) noexcept {
move_only_function(std::move(__other)).swap(*this);
return *this;
}
_LIBCPP_HIDE_FROM_ABI move_only_function& operator=(nullptr_t) noexcept {
this->__reset();
return *this;
}
template <class _Func>
requires(!is_same_v<remove_cvref_t<_Func>, move_only_function> && !__is_inplace_type<_Func>::value &&
__base::template __is_callable_from<_Func>)
_LIBCPP_HIDE_FROM_ABI move_only_function& operator=(_Func&& __func) {
move_only_function(std::forward<_Func>(__func)).swap(*this);
return *this;
}
explicit operator bool() const noexcept { return this->__vtable_ != nullptr; }
using __base::operator();
// [func.wrap.move.util]
_LIBCPP_HIDE_FROM_ABI void swap(move_only_function& __other) noexcept {
std::swap(this->__vtable_, __other.__vtable_);
std::swap(this->__buffer_, __other.__buffer_);
}
_LIBCPP_HIDE_FROM_ABI friend void swap(move_only_function& __lhs, move_only_function& __rhs) noexcept {
__lhs.swap(__rhs);
}
_LIBCPP_HIDE_FROM_ABI friend bool operator==(const move_only_function& __func, nullptr_t) noexcept { return !__func; }
};
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H
```
https://github.com/llvm/llvm-project/pull/94670
More information about the llvm-branch-commits
mailing list