Fix null function check for `move_only_function` (#3038)

Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
Alex Guteniev 2022-08-19 00:27:45 +03:00 коммит произвёл GitHub
Родитель ed0e3e5ad9
Коммит 39b29dd049
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
1 изменённых файлов: 34 добавлений и 17 удалений

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

@ -1383,20 +1383,19 @@ public:
// (The RTTI savings may be significant as with lambdas and binds there may be many distinct callable types. // (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.) // Here we don't have a distinct wrapper class for each callable type, only distinct functions when needed.)
// _Move and _Destroy are nullptr if trivial. Besides being an optimization, this enables assigning an
// empty function from a DLL that is unloaded later, and then safely moving/destroying that empty function.
// Calls target // Calls target
typename _Invoke_t<_Noexcept>::_Call _Invoke; typename _Invoke_t<_Noexcept>::_Call _Invoke;
// Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable").
// nullptr if we can trivially move two pointers.
void(__stdcall* _Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; void(__stdcall* _Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR;
// Destroys data (not resetting its "vtable") // Destroys data (not resetting its "vtable").
// nullptr if destruction is a no-op.
void(__stdcall* _Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR; 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_data _Data;
_Move_only_function_base() noexcept = default; // leaves fields uninitialized _Move_only_function_base() noexcept = default; // leaves fields uninitialized
@ -1407,12 +1406,12 @@ public:
} }
void _Construct_with_null() noexcept { void _Construct_with_null() noexcept {
_Data._Impl = &_Null_move_only_function; _Data._Impl = nullptr;
_Data._Set_large_fn_ptr(nullptr); // initialize, since we'll be copying it _Data._Set_large_fn_ptr(nullptr); // initialize, since we'll be copying it
} }
void _Reset_to_null() noexcept { void _Reset_to_null() noexcept {
_Data._Impl = &_Null_move_only_function; _Data._Impl = nullptr;
} }
template <class _Vt, class _VtInvQuals, class... _CTypes> template <class _Vt, class _VtInvQuals, class... _CTypes>
@ -1426,14 +1425,14 @@ public:
} }
static void _Checked_destroy(_Move_only_function_data& _Data) noexcept { static void _Checked_destroy(_Move_only_function_data& _Data) noexcept {
const auto _Impl = static_cast<const _Impl_t*>(_Data._Impl); const auto _Impl = _Get_impl(_Data);
if (_Impl->_Destroy) { if (_Impl->_Destroy) {
_Impl->_Destroy(_Data); _Impl->_Destroy(_Data);
} }
} }
static void _Checked_move(_Move_only_function_data& _Data, _Move_only_function_data& _Src) noexcept { 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); const auto _Impl = _Get_impl(_Src);
if (_Impl->_Move) { if (_Impl->_Move) {
_Impl->_Move(_Data, _Src); _Impl->_Move(_Data, _Src);
} else { } else {
@ -1447,8 +1446,9 @@ public:
// It is more efficient to do the reverse - this way no temporary storage for the old target will be used. // 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, // In some cases when some operations are trivial, it can be optimized,
// as the order change is unobservable, and everything is noexcept here. // 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 = _Get_impl(_Data);
const auto _This_impl_destroy = static_cast<const _Impl_t*>(_Data._Impl)->_Destroy; const auto _Other_impl_move = _Get_impl(_Other._Data)->_Move;
const auto _This_impl_destroy = _This_impl->_Destroy;
if (!_Other_impl_move) { if (!_Other_impl_move) {
// Move is trivial, destroy first if needed // Move is trivial, destroy first if needed
@ -1462,7 +1462,13 @@ public:
} else { } else {
// General case involving a temporary // General case involving a temporary
_Move_only_function_data _Tmp; _Move_only_function_data _Tmp;
_Checked_move(_Tmp, _Data);
if (_This_impl->_Move) {
_This_impl->_Move(_Tmp, _Data);
} else {
_Function_move_large(_Tmp, _Data);
}
_Other_impl_move(_Data, _Other._Data); _Other_impl_move(_Data, _Other._Data);
_This_impl_destroy(_Tmp); _This_impl_destroy(_Tmp);
} }
@ -1478,7 +1484,7 @@ public:
} }
_NODISCARD bool _Is_null() const noexcept { _NODISCARD bool _Is_null() const noexcept {
return _Data._Impl == &_Null_move_only_function; return _Data._Impl == nullptr;
} }
template <class _Vt> template <class _Vt>
@ -1487,7 +1493,18 @@ public:
|| sizeof(_Vt) > _Move_only_function_data::_Buf_size<_Vt> || !is_nothrow_move_constructible_v<_Vt>; || sizeof(_Vt) > _Move_only_function_data::_Buf_size<_Vt> || !is_nothrow_move_constructible_v<_Vt>;
_NODISCARD auto _Get_invoke() const noexcept { _NODISCARD auto _Get_invoke() const noexcept {
return static_cast<const _Impl_t*>(_Data._Impl)->_Invoke; return _Get_impl(_Data)->_Invoke;
}
_NODISCARD static const _Impl_t* _Get_impl(const _Move_only_function_data& _Data) noexcept {
static constexpr _Impl_t _Null_move_only_function = {
_Function_not_callable<_Rx, _Types...>,
nullptr,
nullptr,
};
const auto _Ret = static_cast<const _Impl_t*>(_Data._Impl);
return _Ret ? _Ret : &_Null_move_only_function;
} }
template <class _Vt, class _VtInvQuals> template <class _Vt, class _VtInvQuals>