Co-authored-by: frederick-vs-ja <de34@live.cn>
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
Alex Guteniev 2021-12-17 03:24:28 +02:00 коммит произвёл GitHub
Родитель 9265c51c86
Коммит b33a89ec69
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 1287 добавлений и 15 удалений

Просмотреть файл

@ -1007,7 +1007,7 @@ private:
template <class _Tx>
struct _Get_function_impl {
static_assert(_Always_false<_Tx>, "std::function does not accept non-function types as template arguments.");
static_assert(_Always_false<_Tx>, "std::function only accepts function types as template arguments.");
};
#define _GET_FUNCTION_IMPL(CALL_OPT, X1, X2, X3) \
@ -1207,6 +1207,649 @@ _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept {
}
#endif // !_HAS_CXX20
#if _HAS_CXX23
// _Move_only_function_data is defined as an array of pointers.
// The first element is always a pointer to _Move_only_function_base::_Impl_t; it emulates a vtable pointer.
// The other pointers are used as storage for a small functor;
// if the functor does not fit in, the second pointer is the pointer to allocated storage, the rest are unused.
union alignas(max_align_t) _Move_only_function_data {
void* _Pointers[_Small_object_num_ptrs];
const void* _Impl;
char _Data; // For aliasing
template <class _Fn>
static constexpr size_t _Buf_offset = alignof(_Fn) <= sizeof(_Impl)
? sizeof(_Impl) // Store _Fn immediately after _Impl
: alignof(_Fn); // Pad _Fn to next alignment
template <class _Fn>
static constexpr size_t _Buf_size = sizeof(_Pointers) - _Buf_offset<_Fn>;
template <class _Fn>
_NODISCARD void* _Buf_ptr() noexcept {
return &_Data + _Buf_offset<_Fn>;
}
template <class _Fn>
_NODISCARD _Fn* _Small_fn_ptr() const noexcept {
// cast away const to avoid complication of const propagation to here;
// const correctness is still enforced by _Move_only_function_call specializations.
return static_cast<_Fn*>(const_cast<_Move_only_function_data*>(this)->_Buf_ptr<_Fn>());
}
template <class _Fn>
_NODISCARD _Fn* _Large_fn_ptr() const noexcept {
return static_cast<_Fn*>(_Pointers[1]);
}
void _Set_large_fn_ptr(void* const _Value) noexcept {
_Pointers[1] = _Value;
}
};
// Size of a large function. Treat an empty function as if it has this size.
// Treat a small function as if it has this size too if it fits and is trivially copyable.
inline constexpr size_t _Minimum_function_size = 2 * sizeof(void*);
// The below functions are __stdcall as they are called by pointers from _Move_only_function_base::_Impl_t.
// (We use explicit __stdcall to make the ABI stable for translation units with different calling convention options.)
// Non-template functions are still defined inline, as the compiler may be able to devirtualize some calls.
template <class _Rx, class... _Types>
[[noreturn]] _Rx __stdcall _Function_not_callable(const _Move_only_function_data&, _Types&&...) noexcept {
_CSTD abort(); // Unlike std::function, move_only_function doesn't throw bad_function_call
// (N4901 [func.wrap.move.inv]/2)
}
template <class _Vt, class _VtInvQuals, class _Rx, bool _Noex, class... _Types>
_NODISCARD _Rx __stdcall _Function_inv_small(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) {
return _Invoker_ret<_Rx>::_Call(
static_cast<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
}
template <class _Vt, class _VtInvQuals, class _Rx, bool _Noex, class... _Types>
_NODISCARD _Rx __stdcall _Function_inv_large(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) {
return _Invoker_ret<_Rx>::_Call(
static_cast<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...);
}
template <class _Vt>
void __stdcall _Function_move_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept {
const auto _Src_fn_ptr = _Src._Small_fn_ptr<_Vt>();
::new (_Self._Buf_ptr<_Vt>()) _Vt(_STD move(*_Src_fn_ptr));
_Src_fn_ptr->~_Vt();
_Self._Impl = _Src._Impl;
}
template <size_t _Size>
void __stdcall _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept {
_CSTD memcpy(&_Self._Data, &_Src._Data, _Size); // Copy Impl* and functor data
}
inline void __stdcall _Function_move_large(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept {
_CSTD memcpy(&_Self._Data, &_Src._Data, _Minimum_function_size); // Copy Impl* and functor data
}
template <class _Vt>
void __stdcall _Function_destroy_small(_Move_only_function_data& _Self) noexcept {
_Self._Small_fn_ptr<_Vt>()->~_Vt();
}
inline void __stdcall _Function_deallocate_large_default_aligned(_Move_only_function_data& _Self) noexcept {
::operator delete(_Self._Large_fn_ptr<void>());
}
template <size_t _Align>
void __stdcall _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noexcept {
::operator delete (_Self._Large_fn_ptr<void>(), align_val_t{_Align});
}
template <class _Vt>
void __stdcall _Function_destroy_large(_Move_only_function_data& _Self) noexcept {
const auto _Pfn = _Self._Large_fn_ptr<_Vt>();
_Pfn->~_Vt();
if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
::operator delete(static_cast<void*>(_Pfn));
} else {
::operator delete (static_cast<void*>(_Pfn), align_val_t{alignof(_Vt)});
}
}
template <class _Vt>
inline constexpr size_t _Function_small_copy_size = // We copy Impl* and the functor data at once
_Move_only_function_data::_Buf_offset<_Vt> + // Impl* plus possible alignment
(size_t{sizeof(_Vt) + sizeof(void*) - 1} & ~size_t{sizeof(void*) - 1}); // size in whole pointers
template <class _Vt, class... _CTypes>
_NODISCARD void* _Function_new_large(_CTypes&&... _Args) {
struct _NODISCARD _Guard_type {
void* _Ptr;
~_Guard_type() {
// _Ptr is not nullptr only if an exception is thrown as a result of _Vt construction.
// Check _Ptr before calling operator delete to save the call in the common case.
if (_Ptr) {
if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
::operator delete(_Ptr);
} else {
::operator delete (_Ptr, align_val_t{alignof(_Vt)});
}
}
}
};
void* _Ptr;
if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
_Ptr = ::operator new(sizeof(_Vt));
} else {
_Ptr = ::operator new (sizeof(_Vt), align_val_t{alignof(_Vt)});
}
_Guard_type _Guard{_Ptr};
::new (_Ptr) _Vt(_STD forward<_CTypes>(_Args)...);
_Guard._Ptr = nullptr;
return _Ptr;
}
template <class _Rx, bool _Noexcept, class... _Types>
class _Move_only_function_base {
public:
// TRANSITION, DevCom-1208330: use noexcept(_Noexcept) instead
template <bool>
struct _Invoke_t {
using _Call = _Rx(__stdcall*)(const _Move_only_function_data&, _Types&&...);
};
template <>
struct _Invoke_t<true> {
using _Call = _Rx(__stdcall*)(const _Move_only_function_data&, _Types&&...) _NOEXCEPT_FNPTR;
};
struct _Impl_t { // A per-callable-type structure acting as a virtual function table.
// Using vtable emulations gives more flexibility for optimizations and reduces the amount of RTTI data.
// (The RTTI savings may be significant as with lambdas and binds there may be many distinct callable types.
// Here we don't have a distinct wrapper class for each callable type, only distinct functions when needed.)
// Calls target
typename _Invoke_t<_Noexcept>::_Call _Invoke;
// Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable")
void(__stdcall* _Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR;
// Destroys data (not resetting its "vtable")
void(__stdcall* _Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR;
};
static constexpr _Impl_t _Null_move_only_function = {
_Function_not_callable<_Rx, _Types...>,
nullptr,
nullptr,
};
_Move_only_function_data _Data;
_Move_only_function_base() noexcept = default; // leaves fields uninitialized
_Move_only_function_base(_Move_only_function_base&& _Other) noexcept {
_Checked_move(_Data, _Other._Data);
_Other._Reset_to_null();
}
void _Construct_with_null() noexcept {
_Data._Impl = &_Null_move_only_function;
_Data._Set_large_fn_ptr(nullptr); // initialize, since we'll be copying it
}
void _Reset_to_null() noexcept {
_Data._Impl = &_Null_move_only_function;
}
template <class _Vt, class _VtInvQuals, class... _CTypes>
void _Construct_with_fn(_CTypes&&... _Args) {
_Data._Impl = _Create_impl_ptr<_Vt, _VtInvQuals>();
if constexpr (_Large_function_engaged<_Vt>) {
_Data._Set_large_fn_ptr(_Function_new_large<_Vt>(_STD forward<_CTypes>(_Args)...));
} else {
::new (_Data._Buf_ptr<_Vt>()) _Vt(_STD forward<_CTypes>(_Args)...);
}
}
static void _Checked_destroy(_Move_only_function_data& _Data) noexcept {
const auto _Impl = static_cast<const _Impl_t*>(_Data._Impl);
if (_Impl->_Destroy) {
_Impl->_Destroy(_Data);
}
}
static void _Checked_move(_Move_only_function_data& _Data, _Move_only_function_data& _Src) noexcept {
const auto _Impl = static_cast<const _Impl_t*>(_Src._Impl);
if (_Impl->_Move) {
_Impl->_Move(_Data, _Src);
} else {
_Function_move_large(_Data, _Src);
}
}
void _Move_assign(_Move_only_function_base&& _Other) noexcept {
// As specified in N4901 [func.wrap.move.ctor]/22, we are expected to first move the new target,
// then finally destroy the old target.
// It is more efficient to do the reverse - this way no temporary storage for the old target will be used.
// In some cases when some operations are trivial, it can be optimized,
// as the order change is unobservable, and everything is noexcept here.
const auto _Other_impl_move = static_cast<const _Impl_t*>(_Other._Data._Impl)->_Move;
const auto _This_impl_destroy = static_cast<const _Impl_t*>(_Data._Impl)->_Destroy;
if (!_Other_impl_move) {
// Move is trivial, destroy first if needed
if (_This_impl_destroy) {
_This_impl_destroy(_Data);
}
_Function_move_large(_Data, _Other._Data);
} else if (!_This_impl_destroy) {
// Destroy is trivial, just move
_Other_impl_move(_Data, _Other._Data);
} else {
// General case involving a temporary
_Move_only_function_data _Tmp;
_Checked_move(_Tmp, _Data);
_Other_impl_move(_Data, _Other._Data);
_This_impl_destroy(_Tmp);
}
_Other._Reset_to_null();
}
void _Swap(_Move_only_function_base& _Other) noexcept {
_Move_only_function_data _Tmp;
_Checked_move(_Tmp, _Data);
_Checked_move(_Data, _Other._Data);
_Checked_move(_Other._Data, _Tmp);
}
_NODISCARD bool _Is_null() const noexcept {
return _Data._Impl == &_Null_move_only_function;
}
template <class _Vt>
static constexpr bool _Large_function_engaged =
alignof(_Vt) > alignof(max_align_t)
|| sizeof(_Vt) > _Move_only_function_data::_Buf_size<_Vt> || !is_nothrow_move_constructible_v<_Vt>;
_NODISCARD auto _Get_invoke() const noexcept {
return static_cast<const _Impl_t*>(_Data._Impl)->_Invoke;
}
template <class _Vt, class _VtInvQuals>
_NODISCARD static constexpr _Impl_t _Create_impl() noexcept {
_Impl_t _Impl{};
if constexpr (_Large_function_engaged<_Vt>) {
_Impl._Invoke = _Function_inv_large<_Vt, _VtInvQuals, _Rx, _Noexcept, _Types...>;
_Impl._Move = nullptr;
if constexpr (is_trivially_destructible_v<_Vt>) {
if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
_Impl._Destroy = _Function_deallocate_large_default_aligned;
} else {
_Impl._Destroy = _Function_deallocate_large_overaligned<alignof(_Vt)>;
}
} else {
_Impl._Destroy = _Function_destroy_large<_Vt>;
}
} else {
_Impl._Invoke = _Function_inv_small<_Vt, _VtInvQuals, _Rx, _Noexcept, _Types...>;
if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) {
if constexpr ((_Function_small_copy_size<_Vt>) > _Minimum_function_size) {
_Impl._Move = _Function_move_memcpy<_Function_small_copy_size<_Vt>>;
} else {
_Impl._Move = nullptr;
}
} else {
_Impl._Move = _Function_move_small<_Vt>;
}
if constexpr (is_trivially_destructible_v<_Vt>) {
_Impl._Destroy = nullptr;
} else {
_Impl._Destroy = _Function_destroy_small<_Vt>;
}
}
return _Impl;
}
template <class _Vt, class _VtInvQuals>
_NODISCARD static const _Impl_t* _Create_impl_ptr() noexcept {
static constexpr _Impl_t _Impl = _Create_impl<_Vt, _VtInvQuals>();
return &_Impl;
}
};
template <class... _Signature>
class _Move_only_function_call {
static_assert((_Always_false<_Signature> || ...),
"std::move_only_function only accepts function types as template arguments, "
"with possibly const/ref/noexcept qualifiers.");
static_assert(sizeof...(_Signature) > 0,
"Unlike std::function, std::move_only_function does not define class template argument deduction guides.");
};
// A script to generate the specializations is at
// /tools/move_only_function_specializations/move_only_function_specializations.py
// (Avoiding C++ preprocessor for better IDE navigation and debugging experience)
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...)> : public _Move_only_function_base<_Rx, false, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = _Vt&;
template <class _Vt>
static constexpr bool _Is_callable_from =
is_invocable_r_v<_Rx, _Vt, _Types...>&& is_invocable_r_v<_Rx, _Vt&, _Types...>;
_Rx operator()(_Types... _Args) {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...)&> : public _Move_only_function_base<_Rx, false, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = _Vt&;
template <class _Vt>
static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&, _Types...>;
_Rx operator()(_Types... _Args) & {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...) &&> : public _Move_only_function_base<_Rx, false, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = _Vt&&;
template <class _Vt>
static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt, _Types...>;
_Rx operator()(_Types... _Args) && {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...) const> : public _Move_only_function_base<_Rx, false, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = const _Vt&;
template <class _Vt>
static constexpr bool _Is_callable_from =
is_invocable_r_v<_Rx, const _Vt, _Types...>&& is_invocable_r_v<_Rx, const _Vt&, _Types...>;
_Rx operator()(_Types... _Args) const {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...) const&> : public _Move_only_function_base<_Rx, false, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = const _Vt&;
template <class _Vt>
static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, const _Vt&, _Types...>;
_Rx operator()(_Types... _Args) const& {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...) const&&> : public _Move_only_function_base<_Rx, false, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = const _Vt&&;
template <class _Vt>
static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, const _Vt, _Types...>;
_Rx operator()(_Types... _Args) const&& {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
#ifdef __cpp_noexcept_function_type
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...) noexcept> : public _Move_only_function_base<_Rx, true, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = _Vt&;
template <class _Vt>
static constexpr bool _Is_callable_from =
is_nothrow_invocable_r_v<_Rx, _Vt, _Types...>&& is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>;
_Rx operator()(_Types... _Args) noexcept {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...)& noexcept> : public _Move_only_function_base<_Rx, true, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = _Vt&;
template <class _Vt>
static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>;
_Rx operator()(_Types... _Args) & noexcept {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...)&& noexcept> : public _Move_only_function_base<_Rx, true, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = _Vt&&;
template <class _Vt>
static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt, _Types...>;
_Rx operator()(_Types... _Args) && noexcept {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...) const noexcept> : public _Move_only_function_base<_Rx, true, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = const _Vt&;
template <class _Vt>
static constexpr bool _Is_callable_from =
is_nothrow_invocable_r_v<_Rx, const _Vt, _Types...>&& is_nothrow_invocable_r_v<_Rx, const _Vt&, _Types...>;
_Rx operator()(_Types... _Args) const noexcept {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...) const& noexcept> : public _Move_only_function_base<_Rx, true, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = const _Vt&;
template <class _Vt>
static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, const _Vt&, _Types...>;
_Rx operator()(_Types... _Args) const& noexcept {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...) const&& noexcept>
: public _Move_only_function_base<_Rx, true, _Types...> {
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = const _Vt&&;
template <class _Vt>
static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, const _Vt, _Types...>;
_Rx operator()(_Types... _Args) const&& noexcept {
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}
};
#endif // __cpp_noexcept_function_type
template <class... _Signature>
class move_only_function : private _Move_only_function_call<_Signature...> {
private:
using _Call = _Move_only_function_call<_Signature...>;
// clang-format off
template <class _Fn>
static constexpr bool _Enable_one_arg_constructor =
!is_same_v<remove_cvref_t<_Fn>, move_only_function>
&& !_Is_specialization_v<remove_cvref_t<_Fn>, in_place_type_t>
&& _Call::template _Is_callable_from<decay_t<_Fn>>;
template <class _Fn, class... _CTypes>
static constexpr bool _Enable_in_place_constructor =
is_constructible_v<decay_t<_Fn>, _CTypes...>
&& _Call::template _Is_callable_from<decay_t<_Fn>>;
template <class _Fn, class _Ux, class... _CTypes>
static constexpr bool _Enable_in_place_list_constructor =
is_constructible_v<decay_t<_Fn>, initializer_list<_Ux>&, _CTypes...>
&& _Call::template _Is_callable_from<decay_t<_Fn>>;
// clang-format on
public:
using typename _Call::result_type;
move_only_function() noexcept {
this->_Construct_with_null();
}
move_only_function(nullptr_t) noexcept {
this->_Construct_with_null();
}
move_only_function(move_only_function&&) noexcept = default;
template <class _Fn, enable_if_t<_Enable_one_arg_constructor<_Fn>, int> = 0>
move_only_function(_Fn&& _Callable) {
using _Vt = decay_t<_Fn>;
static_assert(is_constructible_v<_Vt, _Fn>, "_Vt should be constructible from _Fn. "
"(N4901 [func.wrap.move.ctor]/6)");
if constexpr (is_member_pointer_v<_Vt> || is_pointer_v<_Vt> || _Is_specialization_v<_Vt, move_only_function>) {
if (_Callable == nullptr) {
this->_Construct_with_null();
return;
}
}
using _VtInvQuals = typename _Call::template _VtInvQuals<_Vt>;
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable));
}
template <class _Fn, class... _CTypes, enable_if_t<_Enable_in_place_constructor<_Fn, _CTypes...>, int> = 0>
explicit move_only_function(in_place_type_t<_Fn>, _CTypes&&... _Args) {
using _Vt = decay_t<_Fn>;
static_assert(is_same_v<_Vt, _Fn>, "_Vt should be the same type as _Fn. (N4901 [func.wrap.move.ctor]/12)");
using _VtInvQuals = typename _Call::template _VtInvQuals<_Vt>;
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_CTypes>(_Args)...);
}
template <class _Fn, class _Ux, class... _CTypes,
enable_if_t<_Enable_in_place_list_constructor<_Fn, _Ux, _CTypes...>, int> = 0>
explicit move_only_function(in_place_type_t<_Fn>, initializer_list<_Ux> _Li, _CTypes&&... _Args) {
using _Vt = decay_t<_Fn>;
static_assert(is_same_v<_Vt, _Fn>, "_Vt should be the same type as _Fn. (N4901 [func.wrap.move.ctor]/18)");
using _VtInvQuals = typename _Call::template _VtInvQuals<_Vt>;
this->template _Construct_with_fn<_Vt, _VtInvQuals>(_Li, _STD forward<_CTypes>(_Args)...);
}
~move_only_function() {
// Do cleanup in this class destructor rather than base,
// so that if object construction throws, the unnecessary cleanup isn't called.
this->_Checked_destroy(this->_Data);
}
move_only_function& operator=(nullptr_t) noexcept {
this->_Checked_destroy(this->_Data);
this->_Reset_to_null();
return *this;
}
move_only_function& operator=(move_only_function&& _Other) {
if (this != _STD addressof(_Other)) {
this->_Move_assign(_STD move(_Other));
}
return *this;
}
template <class _Fn, enable_if_t<is_constructible_v<move_only_function, _Fn>, int> = 0>
move_only_function& operator=(_Fn&& _Callable) {
this->_Move_assign(move_only_function{_STD forward<_Fn>(_Callable)});
return *this;
}
_NODISCARD explicit operator bool() const noexcept {
return !this->_Is_null();
}
using _Call::operator();
void swap(move_only_function& _Other) noexcept {
this->_Swap(_Other);
}
friend void swap(move_only_function& _Fn1, move_only_function& _Fn2) noexcept {
_Fn1._Swap(_Fn2);
}
_NODISCARD friend bool operator==(const move_only_function& _This, nullptr_t) noexcept {
return _This._Is_null();
}
};
#endif // _HAS_CXX23
template <int _Nx>
struct _Ph { // placeholder
static_assert(_Nx > 0, "invalid placeholder index");

Просмотреть файл

@ -279,6 +279,7 @@
// Other C++20 deprecation warnings
// _HAS_CXX23 directly controls:
// P0288R9 move_only_function
// P0401R6 Providing Size Feedback In The Allocator Interface
// P0448R4 <spanstream>
// P0943R6 Supporting C Atomics In C++
@ -1381,9 +1382,10 @@
#define __cpp_lib_allocate_at_least 202106L
#endif // __cpp_lib_concepts
#define __cpp_lib_byteswap 202110L
#define __cpp_lib_invoke_r 202106L
#define __cpp_lib_is_scoped_enum 202011L
#define __cpp_lib_byteswap 202110L
#define __cpp_lib_invoke_r 202106L
#define __cpp_lib_is_scoped_enum 202011L
#define __cpp_lib_move_only_function 202110L
#ifdef __cpp_lib_concepts
#define __cpp_lib_out_ptr 202106L

Просмотреть файл

@ -244,6 +244,7 @@ tests\P0220R1_polymorphic_memory_resources
tests\P0220R1_sample
tests\P0220R1_searchers
tests\P0220R1_string_view
tests\P0288R9_move_only_function
tests\P0325R4_to_array
tests\P0339R6_polymorphic_allocator
tests\P0355R7_calendars_and_time_zones_clocks

Просмотреть файл

@ -43,16 +43,15 @@ struct not_overaligned_t {
static_assert(alignof(overaligned_t) > alignof(std::max_align_t), "overaligned_t is not overaligned");
using function_t = std::function<void(const void* storage, std::size_t storage_size)>;
template <class function_t>
void test() {
struct functions_t {
function_t first{overaligned_t{}};
char smallest_pad;
function_t second{overaligned_t{}};
function_t third{overaligned_t{}};
};
struct functions_t {
function_t first{overaligned_t{}};
char smallest_pad;
function_t second{overaligned_t{}};
function_t third{overaligned_t{}};
};
int main() {
functions_t functions;
functions.first(&functions.first, sizeof(functions.first));
functions.second(&functions.second, sizeof(functions.second));
@ -60,6 +59,12 @@ int main() {
function_t sfo{not_overaligned_t{}};
sfo(&sfo, sizeof(sfo));
return 0;
}
int main() {
test<std::function<void(const void* storage, std::size_t storage_size)>>();
#ifdef __cpp_lib_move_only_function
test<std::move_only_function<void(const void* storage, std::size_t storage_size)>>();
#endif
}

Просмотреть файл

@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
RUNALL_INCLUDE ..\usual_latest_matrix.lst

Просмотреть файл

@ -0,0 +1,563 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <cassert>
#include <cstdlib>
#include <functional>
#include <new>
#include <stdexcept>
#include <type_traits>
#include <utility>
using namespace std;
constexpr auto large_function_size = 100;
struct pass_this_by_ref {
int v;
pass_this_by_ref(int v_) : v(v_) {}
pass_this_by_ref(const pass_this_by_ref&) {
abort();
}
};
struct counter {
static int inst;
static int copies;
static int moves;
counter() {
++inst;
}
counter(const counter&) {
++inst;
++copies;
}
counter(counter&&) noexcept {
++inst;
++moves;
}
~counter() {
--inst;
}
};
int counter::inst = 0;
int counter::copies = 0;
int counter::moves = 0;
struct small_callable : counter {
int operator()(int a, pass_this_by_ref& b) {
assert(a == 23);
assert(b.v == 63);
return 38;
}
void* operator new(size_t) = delete;
void operator delete(void*) = delete;
small_callable() = default;
small_callable(const small_callable&) = default;
small_callable(small_callable&&) noexcept = default;
};
struct large_callable : counter {
char data[large_function_size] = {};
int operator()(int a, pass_this_by_ref& b) {
assert(a == 23);
assert(b.v == 63);
return 39;
}
void* operator new(size_t) = delete;
void operator delete(void*) = delete;
large_callable() = default;
large_callable(const large_callable&) = default;
large_callable(large_callable&&) noexcept = default;
};
struct odd_cc_callable : counter {
int __fastcall operator()(int a, pass_this_by_ref& b) {
assert(a == 23);
assert(b.v == 63);
return 40;
}
odd_cc_callable() = default;
odd_cc_callable(const odd_cc_callable&) = default;
odd_cc_callable(odd_cc_callable&&) noexcept = default;
};
struct large_implicit_ptr_callable : counter {
char data[large_function_size] = {};
using pfn = int (*)(int a, pass_this_by_ref& b);
operator pfn() {
return [](int a, pass_this_by_ref& b) {
assert(a == 23);
assert(b.v == 63);
return 41;
};
}
large_implicit_ptr_callable() = default;
large_implicit_ptr_callable(const large_implicit_ptr_callable&) = default;
large_implicit_ptr_callable(large_implicit_ptr_callable&&) noexcept = default;
};
int __fastcall plain_callable(int a, pass_this_by_ref& b) {
assert(a == 23);
assert(b.v == 63);
return 42;
}
using test_function_t = move_only_function<int(int, pass_this_by_ref&)>;
template <class F, class... Args>
void test_construct_impl(int expect, Args... args) {
{
pass_this_by_ref x{63};
test_function_t constructed_directly(F{args...});
assert(constructed_directly(23, x) == expect);
assert(constructed_directly);
assert(constructed_directly != nullptr);
test_function_t move_constructed = move(constructed_directly);
assert(move_constructed(23, x) == expect);
if constexpr (is_class_v<F>) {
assert(counter::copies == 0);
}
F v{args...};
test_function_t constructed_lvalue(v);
if constexpr (is_class_v<F>) {
assert(counter::copies == 1);
counter::copies = 0;
}
if constexpr (is_class_v<F>) {
counter::copies = 0;
counter::moves = 0;
}
test_function_t constructed_in_place(in_place_type<F>, args...);
assert(constructed_in_place(23, x) == expect);
if constexpr (is_class_v<F>) {
assert(counter::copies == 0);
assert(counter::moves == 0);
}
}
if constexpr (is_class_v<F>) {
assert(counter::inst == 0);
}
}
void test_assign() {
pass_this_by_ref x{63};
{
test_function_t f1{small_callable{}};
test_function_t f2{large_callable{}};
f2 = move(f1);
assert(f2(23, x) == 38);
f1 = large_callable{};
assert(f1(23, x) == 39);
}
{
test_function_t f1{large_callable{}};
test_function_t f2{small_callable{}};
f2 = move(f1);
assert(f2(23, x) == 39);
f1 = small_callable{};
assert(f1(23, x) == 38);
}
{
test_function_t f1{small_callable{}};
test_function_t f2{odd_cc_callable{}};
f2 = move(f1);
assert(f2(23, x) == 38);
f1 = odd_cc_callable{};
assert(f1(23, x) == 40);
}
{
test_function_t f1{large_callable{}};
test_function_t f2{large_implicit_ptr_callable{}};
f2 = move(f1);
assert(f2(23, x) == 39);
f1 = large_implicit_ptr_callable{};
assert(f1(23, x) == 41);
}
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wself-move"
#endif // __clang__
{
test_function_t f1{small_callable{}};
test_function_t f2{large_callable{}};
f1 = move(f1); // deliberate self-move as a test case
#pragma warning(suppress : 26800) // use a moved-from object
assert(f1(23, x) == 38);
f2 = move(f2); // deliberate self-move as a test case
#pragma warning(suppress : 26800) // use a moved-from object
assert(f2(23, x) == 39);
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif // __clang__
{
test_function_t f1{small_callable{}};
test_function_t f2{large_callable{}};
test_function_t f3{small_callable{}};
test_function_t f4{large_callable{}};
test_function_t f5{nullptr};
assert(f1);
assert(f2);
assert(f3);
assert(f4);
assert(!f5);
f1 = nullptr;
f2 = nullptr;
f3 = test_function_t{nullptr};
f4 = test_function_t{nullptr};
assert(!f1);
assert(!f2);
assert(!f3);
assert(!f4);
}
}
void test_swap() {
pass_this_by_ref x{63};
{
test_function_t f1{small_callable{}};
test_function_t f2{large_callable{}};
swap(f1, f2);
assert(f2(23, x) == 38);
assert(f1(23, x) == 39);
}
{
test_function_t f1{small_callable{}};
test_function_t f2{odd_cc_callable{}};
f1.swap(f2);
assert(f2(23, x) == 38);
assert(f1(23, x) == 40);
}
{
test_function_t f1{large_callable{}};
test_function_t f2{large_implicit_ptr_callable{}};
f2.swap(f1);
assert(f2(23, x) == 39);
assert(f1(23, x) == 41);
}
{
test_function_t f1{small_callable{}};
test_function_t f2{large_callable{}};
swap(f1, f1);
f2.swap(f2);
assert(f1(23, x) == 38);
assert(f2(23, x) == 39);
}
}
void test_empty() {
test_function_t no_callable;
assert(!no_callable);
assert(no_callable == nullptr);
assert(nullptr == no_callable);
test_function_t no_callable_moved = move(no_callable);
#pragma warning(suppress : 26800) // use a moved-from object
assert(!no_callable);
assert(no_callable == nullptr);
assert(!no_callable_moved);
assert(no_callable_moved == nullptr);
}
void test_ptr() {
struct s_t {
int f(int p) {
return p + 2;
}
int j = 6;
static int g(int z) {
return z - 3;
}
};
move_only_function<int(s_t*, int)> mem_fun_ptr(&s_t::f);
move_only_function<int(s_t*)> mem_ptr(&s_t::j);
move_only_function<int(int)> fun_ptr(&s_t::g);
s_t s;
assert(mem_fun_ptr);
assert(mem_fun_ptr(&s, 3) == 5);
assert(mem_ptr);
assert(mem_ptr(&s) == 6);
assert(fun_ptr);
assert(fun_ptr(34) == 31);
move_only_function<int(s_t*, int)> mem_fun_ptr_n(static_cast<decltype(&s_t::f)>(nullptr));
move_only_function<int(s_t*)> mem_ptr_n(static_cast<decltype(&s_t::j)>(nullptr));
move_only_function<int(int)> fun_ptr_n(static_cast<decltype(&s_t::g)>(nullptr));
assert(!mem_fun_ptr_n);
assert(!mem_ptr_n);
assert(!fun_ptr_n);
}
void test_inner() {
move_only_function<short(long, long)> f1(nullptr);
move_only_function<int(int, int)> f2 = move(f1);
assert(!f2);
#pragma warning(suppress : 26800) // use a moved-from object
f2 = move(f1);
assert(!f1);
}
void test_inplace_list() {
struct in_place_list_constructible {
in_place_list_constructible(initializer_list<int> li) {
int x = 0;
for (int i : li) {
++x;
assert(x == i);
}
}
in_place_list_constructible(initializer_list<int> li, const char*) {
int x = 0;
for (int i : li) {
--x;
assert(x == i);
}
}
in_place_list_constructible(const in_place_list_constructible&) = delete;
in_place_list_constructible& operator=(const in_place_list_constructible&) = delete;
int operator()(int i) {
return i - 1;
}
};
move_only_function<int(int)> f1(in_place_type<in_place_list_constructible>, {1, 2, 3, 4, 5});
assert(f1(5) == 4);
move_only_function<int(int)> f2(in_place_type<in_place_list_constructible>, {-1, -2, -3, -4, -5}, "fox");
assert(f2(8) == 7);
}
template <bool Nx>
struct test_noexcept_t {
int operator()() noexcept(Nx) {
return 888;
}
};
void test_noexcept() {
using f_x = move_only_function<int()>;
using f_nx = move_only_function<int() noexcept>;
static_assert(!noexcept(declval<f_x>()()));
#ifdef __cpp_noexcept_function_type
static_assert(noexcept(declval<f_nx>()()));
#else // ^^^ defined(__cpp_noexcept_function_type) ^^^ / vvv !defined(__cpp_noexcept_function_type) vvv
static_assert(!noexcept(declval<f_nx>()()));
#endif // ^^^ !defined(__cpp_noexcept_function_type) ^^^
static_assert(is_constructible_v<f_x, test_noexcept_t<false>>);
assert(f_x(test_noexcept_t<false>{})() == 888);
static_assert(is_constructible_v<f_x, test_noexcept_t<true>>);
assert(f_x(test_noexcept_t<true>{})() == 888);
#ifdef __cpp_noexcept_function_type
static_assert(!is_constructible_v<f_nx, test_noexcept_t<false>>);
#else // ^^^ defined(__cpp_noexcept_function_type) ^^^ / vvv !defined(__cpp_noexcept_function_type) vvv
static_assert(is_constructible_v<f_nx, test_noexcept_t<false>>);
assert(f_nx(test_noexcept_t<false>{})() == 888);
#endif // ^^^ !defined(__cpp_noexcept_function_type) ^^^
static_assert(is_constructible_v<f_nx, test_noexcept_t<true>>);
assert(f_nx(test_noexcept_t<true>{})() == 888);
}
template <bool>
struct test_const_t {
int operator()() {
return 456;
}
};
template <>
struct test_const_t<true> {
int operator()() const {
return 456;
}
};
void test_const() {
using f_c = move_only_function<int() const>;
using f_nc = move_only_function<int()>;
static_assert(is_constructible_v<f_nc, test_const_t<false>>);
f_nc f1(test_const_t<false>{});
assert(f1() == 456);
static_assert(is_constructible_v<f_nc, test_const_t<true>>);
f_nc f2(test_const_t<true>{});
assert(f2() == 456);
static_assert(!is_constructible_v<f_c, test_const_t<false>>);
static_assert(is_constructible_v<f_c, test_const_t<true>>);
f_c f3(test_const_t<true>{});
assert(f3() == 456);
const f_c f4(test_const_t<true>{});
assert(f4() == 456);
}
void test_qual() {
move_only_function<int(int)> f1([](auto i) { return i + 1; });
assert(f1(1) == 2);
move_only_function<int(int)&> f2([](auto i) { return i + 1; });
assert(f2(2) == 3);
move_only_function<int(int) &&> f3([](auto i) { return i + 1; });
assert(move(f3)(3) == 4);
move_only_function<int(int) const> f1c([](auto i) { return i + 1; });
assert(f1c(4) == 5);
move_only_function<int(int) const&> f2c([](auto i) { return i + 1; });
assert(f2c(5) == 6);
move_only_function<int(int) const&&> f3c([](auto i) { return i + 1; });
assert(move(f3c)(6) == 7);
move_only_function<int(int) noexcept> f1_nx([](auto i) noexcept { return i + 1; });
assert(f1_nx(1) == 2);
move_only_function<int(int)& noexcept> f2_nx([](auto i) noexcept { return i + 1; });
assert(f2_nx(2) == 3);
move_only_function<int(int)&& noexcept> f3_nx([](auto i) noexcept { return i + 1; });
assert(move(f3_nx)(3) == 4);
move_only_function<int(int) const noexcept> f1c_nx([](auto i) noexcept { return i + 1; });
assert(f1c_nx(4) == 5);
move_only_function<int(int) const& noexcept> f2c_nx([](auto i) noexcept { return i + 1; });
assert(f2c_nx(5) == 6);
move_only_function<int(int) const&& noexcept> f3c_nx([](auto i) noexcept { return i + 1; });
assert(move(f3c_nx)(6) == 7);
}
static_assert(is_same_v<move_only_function<void()>::result_type, void>);
static_assert(is_same_v<move_only_function<short(long&) &>::result_type, short>);
static_assert(is_same_v<move_only_function<int(char*) &&>::result_type, int>);
static_assert(is_same_v<move_only_function<void() const>::result_type, void>);
static_assert(is_same_v<move_only_function<short(long&) const&>::result_type, short>);
static_assert(is_same_v<move_only_function<int(char*) const&&>::result_type, int>);
#ifdef __cpp_noexcept_function_type
static_assert(is_same_v<move_only_function<void() noexcept>::result_type, void>);
static_assert(is_same_v<move_only_function<short(long&) & noexcept>::result_type, short>);
static_assert(is_same_v<move_only_function<int(char*) && noexcept>::result_type, int>);
static_assert(is_same_v<move_only_function<void() const noexcept>::result_type, void>);
static_assert(is_same_v<move_only_function<short(long&) const& noexcept>::result_type, short>);
static_assert(is_same_v<move_only_function<int(char*) const&& noexcept>::result_type, int>);
#endif // ^^^ defined(__cpp_noexcept_function_type) ^^^
bool fail_allocations = false;
#pragma warning(suppress : 28251) // Inconsistent annotation for 'new': this instance has no annotations.
void* operator new(size_t size) {
if (fail_allocations) {
throw bad_alloc{};
}
void* result = size > 0 ? malloc(size) : malloc(1);
if (!result) {
throw bad_alloc{};
}
return result;
}
void operator delete(void* p) noexcept {
free(p);
}
void test_except() {
struct throwing {
throwing() = default;
throwing(throwing&&) { // not noexcept to avoid small functor optimization
throw runtime_error{"boo"};
}
void operator()() {}
};
struct not_throwing {
not_throwing() = default;
not_throwing(not_throwing&&) {} // not noexcept to avoid small functor optimization
void operator()() {}
};
try {
move_only_function<void()> f{throwing{}};
assert(false); // unreachable
} catch (runtime_error&) {
}
try {
fail_allocations = true;
move_only_function<void()> f{not_throwing{}};
assert(false); // unreachable
} catch (bad_alloc&) {
fail_allocations = false;
}
}
int main() {
test_construct_impl<small_callable>(38);
test_construct_impl<large_callable>(39);
test_construct_impl<odd_cc_callable>(40);
test_construct_impl<large_implicit_ptr_callable>(41);
test_construct_impl<decltype(&plain_callable)>(42, plain_callable);
test_assign();
test_swap();
test_empty();
test_ptr();
test_inner();
test_inplace_list();
test_noexcept();
test_const();
test_qual();
test_except();
}

Просмотреть файл

@ -1178,6 +1178,20 @@ STATIC_ASSERT(__cpp_lib_memory_resource == 201603L);
#endif
#endif
#if _HAS_CXX23
#ifndef __cpp_lib_move_only_function
#error __cpp_lib_move_only_function is not defined
#elif __cpp_lib_move_only_function != 202110L
#error __cpp_lib_move_only_function is not 202110L
#else
STATIC_ASSERT(__cpp_lib_move_only_function == 202110L);
#endif
#else
#ifdef __cpp_lib_move_only_function
#error __cpp_lib_move_only_function is defined
#endif
#endif
#if _HAS_CXX17
#ifndef __cpp_lib_node_extract
#error __cpp_lib_node_extract is not defined

Просмотреть файл

@ -0,0 +1,40 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
template = """template <class _Rx, class... _Types>
class _Move_only_function_call<_Rx(_Types...) {cv} {ref} {noex}>
: public _Move_only_function_base<_Rx, {noex_val}, _Types...> {{
public:
using result_type = _Rx;
template <class _Vt>
using _VtInvQuals = {cv} _Vt {ref_inv};
template <class _Vt>
static constexpr bool _Is_callable_from = {callable};
_Rx operator()(_Types... _Args) {cv} {ref} {noex} {{
return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...);
}}
}};"""
def ref_permutations(cv, noex, noex_val, trait):
callable = "{trait}<_Rx, {cv} _Vt, _Types...> && {trait}<_Rx, {cv} _Vt&, _Types...>".format(trait = trait, cv = cv)
print(template.format(cv = cv, ref = "", ref_inv = "&", noex = noex, noex_val = noex_val, callable = callable))
print("")
callable = "{trait}<_Rx, {cv} _Vt&, _Types...>".format(trait = trait, cv = cv)
print(template.format(cv = cv, ref = "&", ref_inv = "&", noex = noex, noex_val = noex_val, callable = callable))
print("")
callable = "{trait}<_Rx, {cv} _Vt, _Types...>".format(trait = trait, cv = cv)
print(template.format(cv = cv, ref = "&&", ref_inv = "&&", noex = noex, noex_val = noex_val, callable = callable))
def cvref_permutations(noex, noex_val, trait):
ref_permutations("", noex, noex_val, trait)
print("")
ref_permutations("const", noex, noex_val, trait)
cvref_permutations("", "false", "is_invocable_r_v")
print("")
print("#ifdef __cpp_noexcept_function_type")
cvref_permutations("noexcept", "true", "is_nothrow_invocable_r_v")
print("#endif // __cpp_noexcept_function_type")