From 750540320723025f5bc38815c397ed6a47c2715f Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 17 Aug 2022 10:51:24 +0300 Subject: [PATCH 1/4] make empty vTable nullptr --- stl/inc/functional | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 2caf1cf2b9b..8351b12d327 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1391,12 +1391,6 @@ public: 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 @@ -1407,12 +1401,12 @@ public: } 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 } void _Reset_to_null() noexcept { - _Data._Impl = &_Null_move_only_function; + _Data._Impl = nullptr; } template @@ -1426,14 +1420,14 @@ public: } static void _Checked_destroy(_Move_only_function_data& _Data) noexcept { - const auto _Impl = static_cast(_Data._Impl); + const auto _Impl = _Get_impl(_Data); 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(_Src._Impl); + const auto _Impl = _Get_impl(_Src); if (_Impl->_Move) { _Impl->_Move(_Data, _Src); } else { @@ -1447,8 +1441,9 @@ public: // 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(_Other._Data._Impl)->_Move; - const auto _This_impl_destroy = static_cast(_Data._Impl)->_Destroy; + const auto _This_impl = _Get_impl(_Data); + const auto _Other_impl_move = _Get_impl(_Other._Data)->_Move; + const auto _This_impl_destroy = _This_impl->_Destroy; if (!_Other_impl_move) { // Move is trivial, destroy first if needed @@ -1462,7 +1457,13 @@ public: } else { // General case involving a temporary _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); _This_impl_destroy(_Tmp); } @@ -1478,7 +1479,7 @@ public: } _NODISCARD bool _Is_null() const noexcept { - return _Data._Impl == &_Null_move_only_function; + return _Data._Impl == nullptr; } template @@ -1487,7 +1488,18 @@ public: || sizeof(_Vt) > _Move_only_function_data::_Buf_size<_Vt> || !is_nothrow_move_constructible_v<_Vt>; _NODISCARD auto _Get_invoke() const noexcept { - return static_cast(_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 _Impl_t* const _Ret = static_cast(_Data._Impl); + return _Ret ? _Ret : &_Null_move_only_function; } template From 353354cb30abfa59c4b812876bf2916d899953cc Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 17 Aug 2022 11:13:16 +0300 Subject: [PATCH 2/4] +clarity --- stl/inc/functional | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stl/inc/functional b/stl/inc/functional index 8351b12d327..b32b62ff5f1 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1383,11 +1383,16 @@ public: // (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.) + // _Move and _Destroy are nullptr if trivial. Besides optimization, this enables assigning emtpy function from + // a DLL that is unloaded later, and then safely moving/destroying that empty function + // Calls target typename _Invoke_t<_Noexcept>::_Call _Invoke; // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") + // nullptr if trivially move two pointers void(__stdcall* _Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; // Destroys data (not resetting its "vtable") + // nullptr if no op destroy void(__stdcall* _Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR; }; From 3fd6dfc5142e004980e6b31a85ec305a8b03a002 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 17 Aug 2022 19:10:52 -0700 Subject: [PATCH 3/4] Comment cleanups. --- stl/inc/functional | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index b32b62ff5f1..83e50787379 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1383,16 +1383,16 @@ public: // (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.) - // _Move and _Destroy are nullptr if trivial. Besides optimization, this enables assigning emtpy function from - // a DLL that is unloaded later, and then safely moving/destroying that empty function + // _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 typename _Invoke_t<_Noexcept>::_Call _Invoke; - // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") - // nullptr if trivially move two pointers + // 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; - // Destroys data (not resetting its "vtable") - // nullptr if no op destroy + // Destroys data (not resetting its "vtable"). + // nullptr if destruction is a no-op. void(__stdcall* _Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR; }; From 435c14e8ff5920477a6c13923d069bf52387166c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 17 Aug 2022 19:11:00 -0700 Subject: [PATCH 4/4] Use auto. --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 83e50787379..69b4d85d54d 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1503,7 +1503,7 @@ public: nullptr, }; - const _Impl_t* const _Ret = static_cast(_Data._Impl); + const auto _Ret = static_cast(_Data._Impl); return _Ret ? _Ret : &_Null_move_only_function; }