From 9080b5e8992f53070f65cc2bc4815956c3858c43 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 20:50:04 +0300 Subject: [PATCH 001/112] Initial (partial) implementation of #2241 --- stl/CMakeLists.txt | 1 + stl/inc/functional | 168 ++++++++++++++++++ stl/inc/yvals_core.h | 6 +- .../stl_base/stl.files.settings.targets | 1 + stl/src/move_only_function.cpp | 22 +++ tests/std/test.lst | 1 + .../tests/P0288R9_move_only_function/env.lst | 4 + .../tests/P0288R9_move_only_function/test.cpp | 91 ++++++++++ .../test.compile.pass.cpp | 14 ++ 9 files changed, 306 insertions(+), 2 deletions(-) create mode 100644 stl/src/move_only_function.cpp create mode 100644 tests/std/tests/P0288R9_move_only_function/env.lst create mode 100644 tests/std/tests/P0288R9_move_only_function/test.cpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index a0232ab12e1..70775e1f330 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -252,6 +252,7 @@ set(IMPLIB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/filesystem.cpp ${CMAKE_CURRENT_LIST_DIR}/src/format.cpp ${CMAKE_CURRENT_LIST_DIR}/src/locale0_implib.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/move_only_function.cpp ${CMAKE_CURRENT_LIST_DIR}/src/nothrow.cpp ${CMAKE_CURRENT_LIST_DIR}/src/sharedmutex.cpp ${CMAKE_CURRENT_LIST_DIR}/src/syserror_import_lib.cpp diff --git a/stl/inc/functional b/stl/inc/functional index 2db14e9137d..3261ae6f15f 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -28,6 +28,12 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new +_EXTERN_C +void __stdcall __std_function_noop_move(uintptr_t*, uintptr_t*) noexcept; +void __stdcall __std_function_noop_destroy(uintptr_t*) noexcept; +void __stdcall __std_function_move_large(uintptr_t* _Self, uintptr_t* _Src) noexcept; +_END_EXTERN_C + _STD_BEGIN #if _HAS_CXX23 template & _Other) noexcept { } #endif // !_HAS_CXX20 +#ifdef _HAS_CXX23 +template +struct _Move_only_function_impl { + using _Invoke_t = _Rx (*)(uintptr_t* _Self, Args...); + using _Move_t = void (*)(uintptr_t* _Self, uintptr_t* _Src) _NOEXCEPT_FNPTR; + using _Destroy_t = void (*)(uintptr_t* _Self) _NOEXCEPT_FNPTR; + + _Invoke_t _Invoke; + _Move_t _Move; + _Destroy_t _Destroy; +}; + +template +[[noreturn]] _Rx _Function_throw_bad_call(uintptr_t*, Args...) { + _Xbad_function_call(); +} + +template +inline constexpr _Move_only_function_impl<_Rx, Args...> _Null_move_only_function = { + _Function_throw_bad_call<_Rx, Args...>, + __std_function_noop_move, + __std_function_noop_destroy, +}; + +template +_Rx _Function_invoke_small(uintptr_t* _Self, _Types... _Args) { + return reinterpret_cast<_Fn&>(*_Self)(_STD forward<_Types>(_Args)...); +} + +template +_Rx _Function_invoke_large(uintptr_t* _Self, _Types... _Args) { + return (*reinterpret_cast<_Fn*>(*_Self)) (_STD forward<_Types>(_Args)...); +} + +template +void _Function_move_small(uintptr_t* _Self, uintptr_t* _Src) noexcept { + new (reinterpret_cast(_Self)) _Fn(_STD move(*reinterpret_cast<_Fn*>(_Src))); + reinterpret_cast<_Fn&>(*_Src).~_Fn(); +} + +template +void _Function_destroy_small(uintptr_t* _Self) noexcept { + reinterpret_cast<_Fn&>(*_Self).~_Fn(); +} + +template +void _Function_destroy_large(uintptr_t* _Self) noexcept { + delete reinterpret_cast<_Fn*>(*_Self); +} + +template +class _Move_only_function_base { +public: + _Move_only_function_base() noexcept = default; // leaves fields uninitialized + + _Move_only_function_base(_Move_only_function_base&& _Other) noexcept : _Impl(_Other._Impl) { + _Impl->_Move(_Data, _Other._Data); + _Other._Impl = &_Null_move_only_function<_Rx, _Types...>; + } + + void _Construct_with_null() noexcept { + _Impl = &_Null_move_only_function<_Rx, _Types...>; + } + + template + void _Construct_with_fn(_Fn&& _Callable) { + _Impl = _Create_impl_ptr<_Fn>(); + if constexpr (_Large_function_engaged<_Fn>()) { + reinterpret_cast(_Data[0]) = new _Fn(_STD move(_Callable)); + } else { + new (reinterpret_cast(_Data)) _Fn(_STD move(_Callable)); + } + } + + ~_Move_only_function_base() { + _Impl->_Destroy(_Data); + } + + using _Impl_t = _Move_only_function_impl<_Rx, _Types...>; + + alignas(max_align_t) uintptr_t _Data[4]; + const _Impl_t* _Impl; + + template + static constexpr bool _Large_function_engaged() noexcept { + return sizeof(_Fn) > sizeof(_Data) || !is_nothrow_move_constructible_v<_Fn> || alignof(_Fn) > alignof(max_align_t); + } + + template + static constexpr _Impl_t _Create_impl() noexcept { + _Impl_t _Impl{}; + if constexpr (_Large_function_engaged<_Fn>()) { + _Impl._Invoke = _Function_invoke_large<_Fn, _Rx, _Types...>; + _Impl._Move = __std_function_move_large; + _Impl._Destroy = _Function_destroy_large<_Fn>; + } else { + _Impl._Invoke = _Function_invoke_small<_Fn, _Rx, _Types...>; + _Impl._Move = _Function_move_small<_Fn>; + _Impl._Destroy = _Function_destroy_small<_Fn>; + } + return _Impl; + } + + template + static const _Impl_t* _Create_impl_ptr() noexcept { + static constexpr _Impl_t _Impl = _Create_impl<_Fn>(); + return &_Impl; + } +}; + +template +class _Move_only_function_call { + static_assert(_Always_false<_Tx>, "std::move_only_function only accepts functions as template argument (with possibly const/ref/noexcept qualifiers)."); +}; + +template +class _Move_only_function_call<_Rx(_Types...)> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + _Rx operator()(_Types... _Args) { + return this->_Impl->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; + +#ifdef __cpp_noexcept_function_type +template +class _Move_only_function_call<_Rx(_Types...) noexcept> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + _Rx operator()(_Types... _Args) noexcept { + return this->_Impl->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; +#endif + +template +class move_only_function : private _Move_only_function_call<_Fx> { +private: + using _Call = _Move_only_function_call<_Fx>; + +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 + move_only_function(_Fn&& _Callable) { + this->_Construct_with_fn(_STD forward<_Fn>(_Callable)); + } + + using _Call::operator(); +}; +#endif + template struct _Ph { // placeholder static_assert(_Nx > 0, "invalid placeholder index"); diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index e9ba8074f75..dd501e3a9e9 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -263,6 +263,7 @@ // Other C++20 deprecation warnings // _HAS_CXX23 directly controls: +// P0288R9 move_only_function // P0401R6 Providing Size Feedback In The Allocator Interface // P0943R6 Supporting C Atomics In C++ // P1048R1 is_scoped_enum @@ -1368,8 +1369,9 @@ #define __cpp_lib_allocate_at_least 202106L #endif // __cpp_lib_concepts -#define __cpp_lib_invoke_r 202106L -#define __cpp_lib_is_scoped_enum 202011L +#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 diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index 32f06b05871..8f4f9433b94 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -163,6 +163,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(CrtRoot)\github\stl\src\filesystem.cpp; $(CrtRoot)\github\stl\src\format.cpp; $(CrtRoot)\github\stl\src\locale0_implib.cpp; + $(CrtRoot)\github\stl\src\move_only_function.cpp; $(CrtRoot)\github\stl\src\nothrow.cpp; $(CrtRoot)\github\stl\src\sharedmutex.cpp; $(CrtRoot)\github\stl\src\syserror_import_lib.cpp; diff --git a/stl/src/move_only_function.cpp b/stl/src/move_only_function.cpp new file mode 100644 index 00000000000..3b6a4458ae5 --- /dev/null +++ b/stl/src/move_only_function.cpp @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +// This must be as small as possible, because its contents are +// injected into the msvcprt.lib and msvcprtd.lib import libraries. +// Do not include or define anything else here. +// In particular, basic_string must not be included here. + +// these declarations must be in sync with those in + +extern "C" { + +void __stdcall __std_function_noop_move(uintptr_t*, uintptr_t*) noexcept {} +void __stdcall __std_function_noop_destroy(uintptr_t*) noexcept {} + +void __stdcall __std_function_move_large(uintptr_t* _Self, uintptr_t* _Src) { + *_Self = *_Src; +} + +} \ No newline at end of file diff --git a/tests/std/test.lst b/tests/std/test.lst index 685b691e290..d188a7390ab 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -232,6 +232,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 diff --git a/tests/std/tests/P0288R9_move_only_function/env.lst b/tests/std/tests/P0288R9_move_only_function/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0288R9_move_only_function/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp new file mode 100644 index 00000000000..80be92c1fb2 --- /dev/null +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include + +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; + ; + + counter() { + inst++; + } + + counter(const counter&) { + inst++; + } + + counter(counter&&) noexcept { + inst++; + } + + ~counter() { + inst--; + } +}; + +int counter::inst = 0; + +struct small_callable : counter { + int operator()(int a, pass_this_by_ref& b) { + assert(a == 23); + assert(b.v == 63); + return 38; + } + + small_callable() = default; + + small_callable(const small_callable&) { + abort(); + } + + small_callable(small_callable&&) noexcept = default; +}; + +struct large_callable : small_callable { + char data[large_function_size]; +}; + +using test_function_t = move_only_function; + +template +void test_impl() { + { + pass_this_by_ref x{63}; + + test_function_t f1(F{}); + assert(f1(23, x) == 38); + + test_function_t f2 = std::move(f1); + + assert(f2(23, x) == 38); + + try { + f1(24, x); + abort(); + } catch (std::bad_function_call&) { + } + } + assert(counter::inst == 0); +} + + +int main() { + test_impl(); + test_impl(); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index e9f0cf006ca..97ace55920f 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1164,6 +1164,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 From 9c9bf5d3ab926b74ccb363d676a34e434526b506 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 20:57:11 +0300 Subject: [PATCH 002/112] newline --- stl/src/move_only_function.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/src/move_only_function.cpp b/stl/src/move_only_function.cpp index 3b6a4458ae5..09158f84373 100644 --- a/stl/src/move_only_function.cpp +++ b/stl/src/move_only_function.cpp @@ -19,4 +19,4 @@ void __stdcall __std_function_move_large(uintptr_t* _Self, uintptr_t* _Src) { *_Self = *_Src; } -} \ No newline at end of file +} From fac83e45945de2c2e8b756d6922d670d307fbf6e Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 20:59:04 +0300 Subject: [PATCH 003/112] clang format --- stl/inc/functional | 6 ++++-- stl/src/move_only_function.cpp | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 3261ae6f15f..f50258715ba 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1300,7 +1300,8 @@ public: template static constexpr bool _Large_function_engaged() noexcept { - return sizeof(_Fn) > sizeof(_Data) || !is_nothrow_move_constructible_v<_Fn> || alignof(_Fn) > alignof(max_align_t); + return sizeof(_Fn) > sizeof(_Data) + || !is_nothrow_move_constructible_v<_Fn> || alignof(_Fn) > alignof(max_align_t); } template @@ -1327,7 +1328,8 @@ public: template class _Move_only_function_call { - static_assert(_Always_false<_Tx>, "std::move_only_function only accepts functions as template argument (with possibly const/ref/noexcept qualifiers)."); + static_assert(_Always_false<_Tx>, "std::move_only_function only accepts functions as template argument (with " + "possibly const/ref/noexcept qualifiers)."); }; template diff --git a/stl/src/move_only_function.cpp b/stl/src/move_only_function.cpp index 09158f84373..bf118f4b27c 100644 --- a/stl/src/move_only_function.cpp +++ b/stl/src/move_only_function.cpp @@ -18,5 +18,4 @@ void __stdcall __std_function_noop_destroy(uintptr_t*) noexcept {} void __stdcall __std_function_move_large(uintptr_t* _Self, uintptr_t* _Src) { *_Self = *_Src; } - } From ae2578cd2ead2e03526fc171e38503eec2d1d72d Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 21:05:17 +0300 Subject: [PATCH 004/112] fix test pass --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index f50258715ba..d1439c399c0 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1328,7 +1328,7 @@ public: template class _Move_only_function_call { - static_assert(_Always_false<_Tx>, "std::move_only_function only accepts functions as template argument (with " + static_assert(_Always_false<_Fn>, "std::move_only_function only accepts functions as template argument (with " "possibly const/ref/noexcept qualifiers)."); }; From ff787b6274f870a723745d417013f062dd5f1f24 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 21:06:43 +0300 Subject: [PATCH 005/112] proper C++23 check --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index d1439c399c0..16e11ad42cc 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1215,7 +1215,7 @@ _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept { } #endif // !_HAS_CXX20 -#ifdef _HAS_CXX23 +#if _HAS_CXX23 template struct _Move_only_function_impl { using _Invoke_t = _Rx (*)(uintptr_t* _Self, Args...); From 7d677b5b9d6bb592fd80c5cafd091e05af2eb482 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 21:10:27 +0300 Subject: [PATCH 006/112] overalignment test --- .../GH_000690_overaligned_function/test.cpp | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/std/tests/GH_000690_overaligned_function/test.cpp b/tests/std/tests/GH_000690_overaligned_function/test.cpp index 0e5623f2054..d396ff69349 100644 --- a/tests/std/tests/GH_000690_overaligned_function/test.cpp +++ b/tests/std/tests/GH_000690_overaligned_function/test.cpp @@ -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; +template +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>(); +#ifdef __cpp_lib_move_only_function + test>(); +#endif } From 6dbf900a60e02eb3a9bc39c87a3da74b80c2ea52 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 21:14:29 +0300 Subject: [PATCH 007/112] calling convention --- stl/inc/functional | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 16e11ad42cc..dea21865e12 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1218,9 +1218,9 @@ _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept { #if _HAS_CXX23 template struct _Move_only_function_impl { - using _Invoke_t = _Rx (*)(uintptr_t* _Self, Args...); - using _Move_t = void (*)(uintptr_t* _Self, uintptr_t* _Src) _NOEXCEPT_FNPTR; - using _Destroy_t = void (*)(uintptr_t* _Self) _NOEXCEPT_FNPTR; + using _Invoke_t = _Rx(__stdcall*)(uintptr_t* _Self, Args...); + using _Move_t = void(__stdcall*)(uintptr_t* _Self, uintptr_t* _Src) _NOEXCEPT_FNPTR; + using _Destroy_t = void(__stdcall*)(uintptr_t* _Self) _NOEXCEPT_FNPTR; _Invoke_t _Invoke; _Move_t _Move; @@ -1240,28 +1240,28 @@ inline constexpr _Move_only_function_impl<_Rx, Args...> _Null_move_only_function }; template -_Rx _Function_invoke_small(uintptr_t* _Self, _Types... _Args) { +_Rx __stdcall _Function_invoke_small(uintptr_t* _Self, _Types... _Args) { return reinterpret_cast<_Fn&>(*_Self)(_STD forward<_Types>(_Args)...); } template -_Rx _Function_invoke_large(uintptr_t* _Self, _Types... _Args) { +_Rx __stdcall _Function_invoke_large(uintptr_t* _Self, _Types... _Args) { return (*reinterpret_cast<_Fn*>(*_Self)) (_STD forward<_Types>(_Args)...); } template -void _Function_move_small(uintptr_t* _Self, uintptr_t* _Src) noexcept { +void __stdcall _Function_move_small(uintptr_t* _Self, uintptr_t* _Src) noexcept { new (reinterpret_cast(_Self)) _Fn(_STD move(*reinterpret_cast<_Fn*>(_Src))); reinterpret_cast<_Fn&>(*_Src).~_Fn(); } template -void _Function_destroy_small(uintptr_t* _Self) noexcept { +void __stdcall _Function_destroy_small(uintptr_t* _Self) noexcept { reinterpret_cast<_Fn&>(*_Self).~_Fn(); } template -void _Function_destroy_large(uintptr_t* _Self) noexcept { +void __stdcall _Function_destroy_large(uintptr_t* _Self) noexcept { delete reinterpret_cast<_Fn*>(*_Self); } From bb5f066592e4653b31b37737b528c656da680fe0 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 21:14:55 +0300 Subject: [PATCH 008/112] clang format --- tests/std/tests/P0288R9_move_only_function/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 80be92c1fb2..5a80540636b 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -63,7 +63,7 @@ struct large_callable : small_callable { using test_function_t = move_only_function; -template +template void test_impl() { { pass_this_by_ref x{63}; From d0512094e740c908e102356eeffc50761dca63f3 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 21:16:17 +0300 Subject: [PATCH 009/112] calling convention --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index dea21865e12..b03b8d34162 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1228,7 +1228,7 @@ struct _Move_only_function_impl { }; template -[[noreturn]] _Rx _Function_throw_bad_call(uintptr_t*, Args...) { +[[noreturn]] _Rx __stdcall _Function_throw_bad_call(uintptr_t*, Args...) { _Xbad_function_call(); } From c650cc390a9d8916c6b0d2c34bbfdf0ba3516d66 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 21:25:02 +0300 Subject: [PATCH 010/112] clang format --- tests/std/tests/GH_000690_overaligned_function/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/GH_000690_overaligned_function/test.cpp b/tests/std/tests/GH_000690_overaligned_function/test.cpp index d396ff69349..573c14cf762 100644 --- a/tests/std/tests/GH_000690_overaligned_function/test.cpp +++ b/tests/std/tests/GH_000690_overaligned_function/test.cpp @@ -44,7 +44,7 @@ struct not_overaligned_t { static_assert(alignof(overaligned_t) > alignof(std::max_align_t), "overaligned_t is not overaligned"); template -void test(){ +void test() { struct functions_t { function_t first{overaligned_t{}}; char smallest_pad; From 2e3a38d66db0f7a004b1042f58705bd41cb3a1a2 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 21:57:54 +0300 Subject: [PATCH 011/112] optimize copying small function --- stl/inc/functional | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index b03b8d34162..a4858eae98f 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1265,6 +1265,11 @@ void __stdcall _Function_destroy_large(uintptr_t* _Self) noexcept { delete reinterpret_cast<_Fn*>(*_Self); } +template +void __stdcall _Function_move_memcpy(uintptr_t* _Self, uintptr_t* _Src) { + _CSTD memcpy(_Self, _Src, _Size); +} + template class _Move_only_function_base { public: @@ -1304,6 +1309,10 @@ public: || !is_nothrow_move_constructible_v<_Fn> || alignof(_Fn) > alignof(max_align_t); } + static constexpr _Round_up_size(size_t _Size) { + return size_t{_Size + sizeof(uintptr_t) - 1} & ~size_t{sizeof(uintptr_t)}; + } + template static constexpr _Impl_t _Create_impl() noexcept { _Impl_t _Impl{}; @@ -1313,8 +1322,18 @@ public: _Impl._Destroy = _Function_destroy_large<_Fn>; } else { _Impl._Invoke = _Function_invoke_small<_Fn, _Rx, _Types...>; - _Impl._Move = _Function_move_small<_Fn>; - _Impl._Destroy = _Function_destroy_small<_Fn>; + + if constexpr (is_trivially_copyable_v<_Fn> && is_trivially_destructible_v<_Fn>) { + _Impl._Move = _Function_move_memcpy<_Round_up_size(sizeof(_Fn))>; + } else { + _Impl._Move = _Function_move_small<_Fn>; + } + + if constexpr (is_trivially_destructible_v<_Fn>) { + _Impl._Destroy = __std_function_noop_destroy; + } else { + _Impl._Destroy = _Function_destroy_small<_Fn>; + } } return _Impl; } From dfcb9c324052d4aa9cca2c34737d7c9dff12e188 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 22:06:27 +0300 Subject: [PATCH 012/112] abort if not callable --- stl/inc/functional | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index a4858eae98f..11edc8bf2fa 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1228,13 +1228,13 @@ struct _Move_only_function_impl { }; template -[[noreturn]] _Rx __stdcall _Function_throw_bad_call(uintptr_t*, Args...) { - _Xbad_function_call(); +[[noreturn]] _Rx __stdcall _Function_no_callable(uintptr_t*, Args...) { + abort0(); } template inline constexpr _Move_only_function_impl<_Rx, Args...> _Null_move_only_function = { - _Function_throw_bad_call<_Rx, Args...>, + _Function_no_callable<_Rx, Args...>, __std_function_noop_move, __std_function_noop_destroy, }; @@ -1265,7 +1265,7 @@ void __stdcall _Function_destroy_large(uintptr_t* _Self) noexcept { delete reinterpret_cast<_Fn*>(*_Self); } -template +template void __stdcall _Function_move_memcpy(uintptr_t* _Self, uintptr_t* _Src) { _CSTD memcpy(_Self, _Src, _Size); } @@ -1321,14 +1321,14 @@ public: _Impl._Move = __std_function_move_large; _Impl._Destroy = _Function_destroy_large<_Fn>; } else { - _Impl._Invoke = _Function_invoke_small<_Fn, _Rx, _Types...>; - + _Impl._Invoke = _Function_invoke_small<_Fn, _Rx, _Types...>; + if constexpr (is_trivially_copyable_v<_Fn> && is_trivially_destructible_v<_Fn>) { _Impl._Move = _Function_move_memcpy<_Round_up_size(sizeof(_Fn))>; } else { _Impl._Move = _Function_move_small<_Fn>; } - + if constexpr (is_trivially_destructible_v<_Fn>) { _Impl._Destroy = __std_function_noop_destroy; } else { From 47d4af81606b7eaed888cdb5ea9dbe420fd7ddd1 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 22:09:57 +0300 Subject: [PATCH 013/112] abort if not callable --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 11edc8bf2fa..f88cab707e5 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1229,7 +1229,7 @@ struct _Move_only_function_impl { template [[noreturn]] _Rx __stdcall _Function_no_callable(uintptr_t*, Args...) { - abort0(); + _CSTD abort(); } template From 4031980d5b18ba71b2cd5c005cfa2429e1f665a7 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 22:17:46 +0300 Subject: [PATCH 014/112] missing type --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index f88cab707e5..f1d155766b4 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1309,7 +1309,7 @@ public: || !is_nothrow_move_constructible_v<_Fn> || alignof(_Fn) > alignof(max_align_t); } - static constexpr _Round_up_size(size_t _Size) { + static constexpr size_t _Round_up_size(size_t _Size) { return size_t{_Size + sizeof(uintptr_t) - 1} & ~size_t{sizeof(uintptr_t)}; } From b2abdee9da6f0fff42130b6b98eb7f9fcee23670 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 22:19:25 +0300 Subject: [PATCH 015/112] mask --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index f1d155766b4..b498835a836 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1310,7 +1310,7 @@ public: } static constexpr size_t _Round_up_size(size_t _Size) { - return size_t{_Size + sizeof(uintptr_t) - 1} & ~size_t{sizeof(uintptr_t)}; + return size_t{_Size + sizeof(uintptr_t) - 1} & ~size_t{sizeof(uintptr_t) - 1}; } template From 25b3396ce7d9dbecb28d25d9c7880c48b1b6b366 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 10 Oct 2021 22:45:32 +0300 Subject: [PATCH 016/112] fix test pass --- stl/inc/functional | 2 +- tests/std/tests/P0288R9_move_only_function/test.cpp | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index b498835a836..c5e07bfe0a4 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1266,7 +1266,7 @@ void __stdcall _Function_destroy_large(uintptr_t* _Self) noexcept { } template -void __stdcall _Function_move_memcpy(uintptr_t* _Self, uintptr_t* _Src) { +void __stdcall _Function_move_memcpy(uintptr_t* _Self, uintptr_t* _Src) noexcept { _CSTD memcpy(_Self, _Src, _Size); } diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 5a80540636b..7ef63cd1807 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -74,12 +74,6 @@ void test_impl() { test_function_t f2 = std::move(f1); assert(f2(23, x) == 38); - - try { - f1(24, x); - abort(); - } catch (std::bad_function_call&) { - } } assert(counter::inst == 0); } From d2f175f280d1d040cf03c4205355305c34f5ebaf Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 11 Oct 2021 15:30:23 +0300 Subject: [PATCH 017/112] uninline, optimize --- stl/CMakeLists.txt | 1 - stl/inc/functional | 135 ++++++++++++------ .../stl_base/stl.files.settings.targets | 1 - stl/src/move_only_function.cpp | 21 --- 4 files changed, 90 insertions(+), 68 deletions(-) delete mode 100644 stl/src/move_only_function.cpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 70775e1f330..a0232ab12e1 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -252,7 +252,6 @@ set(IMPLIB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/filesystem.cpp ${CMAKE_CURRENT_LIST_DIR}/src/format.cpp ${CMAKE_CURRENT_LIST_DIR}/src/locale0_implib.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/move_only_function.cpp ${CMAKE_CURRENT_LIST_DIR}/src/nothrow.cpp ${CMAKE_CURRENT_LIST_DIR}/src/sharedmutex.cpp ${CMAKE_CURRENT_LIST_DIR}/src/syserror_import_lib.cpp diff --git a/stl/inc/functional b/stl/inc/functional index c5e07bfe0a4..131510b7fcd 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -28,12 +28,6 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new -_EXTERN_C -void __stdcall __std_function_noop_move(uintptr_t*, uintptr_t*) noexcept; -void __stdcall __std_function_noop_destroy(uintptr_t*) noexcept; -void __stdcall __std_function_move_large(uintptr_t* _Self, uintptr_t* _Src) noexcept; -_END_EXTERN_C - _STD_BEGIN #if _HAS_CXX23 template & _Other) noexcept { #endif // !_HAS_CXX20 #if _HAS_CXX23 +union alignas(max_align_t) _Move_only_function_data { + void* _Pointers[_Small_object_num_ptrs]; + const void* _Impl; + char _Data; // For aliasing + + + template + static constexpr size_t _Buf_offset() noexcept { + if constexpr (alignof(_Fn) <= sizeof(_Impl)) { + return sizeof(_Impl); // Data immediately after impl + } else { + return alignof(_Fn); // Pad to next alignment + } + } + + template + static constexpr size_t _Buf_size() noexcept { + return sizeof(_Pointers) - _Buf_offset<_Fn>(); + } + + template + constexpr void* _Buf_ptr() { + return &_Data + _Buf_offset<_Fn>(); + } + + template + _Fn* _Small_fn_ptr() { + return static_cast<_Fn*>(_Buf_ptr<_Fn>()); + } + + template + _Fn* _Large_fn_ptr() { + return static_cast<_Fn*>(_Pointers[1]); + } + + void _Set_large_fn_ptr(void* _Value) { + _Pointers[1] = _Value; + } +}; + template struct _Move_only_function_impl { - using _Invoke_t = _Rx(__stdcall*)(uintptr_t* _Self, Args...); - using _Move_t = void(__stdcall*)(uintptr_t* _Self, uintptr_t* _Src) _NOEXCEPT_FNPTR; - using _Destroy_t = void(__stdcall*)(uintptr_t* _Self) _NOEXCEPT_FNPTR; + using _Invoke_t = _Rx (*)(_Move_only_function_data&, Args...); + using _Move_t = void (*)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; + using _Destroy_t = void (*)(_Move_only_function_data&) _NOEXCEPT_FNPTR; _Invoke_t _Invoke; _Move_t _Move; @@ -1228,89 +1262,99 @@ struct _Move_only_function_impl { }; template -[[noreturn]] _Rx __stdcall _Function_no_callable(uintptr_t*, Args...) { +[[noreturn]] _Rx _Function_no_callable(_Move_only_function_data&, Args...) { _CSTD abort(); } +constexpr void _Function_noop_move(_Move_only_function_data&, _Move_only_function_data&) noexcept {} +constexpr void _Function_noop_destroy(_Move_only_function_data&) noexcept {} + template inline constexpr _Move_only_function_impl<_Rx, Args...> _Null_move_only_function = { _Function_no_callable<_Rx, Args...>, - __std_function_noop_move, - __std_function_noop_destroy, + _Function_noop_move, + _Function_noop_destroy, }; template -_Rx __stdcall _Function_invoke_small(uintptr_t* _Self, _Types... _Args) { - return reinterpret_cast<_Fn&>(*_Self)(_STD forward<_Types>(_Args)...); +_Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types... _Args) { + return (*_Self._Small_fn_ptr<_Fn>()) (_STD forward<_Types>(_Args)...); } template -_Rx __stdcall _Function_invoke_large(uintptr_t* _Self, _Types... _Args) { - return (*reinterpret_cast<_Fn*>(*_Self)) (_STD forward<_Types>(_Args)...); +_Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types... _Args) { + return (*_Self._Large_fn_ptr<_Fn>()) (_STD forward<_Types>(_Args)...); } template -void __stdcall _Function_move_small(uintptr_t* _Self, uintptr_t* _Src) noexcept { - new (reinterpret_cast(_Self)) _Fn(_STD move(*reinterpret_cast<_Fn*>(_Src))); - reinterpret_cast<_Fn&>(*_Src).~_Fn(); +void _Function_move_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { + ::new (_Self._Buf_ptr<_Fn>()) _Fn(_STD move(*_Src._Small_fn_ptr<_Fn>())); + _Src._Small_fn_ptr<_Fn>()->~_Fn(); + _Self._Impl = _Src._Impl; +} + +inline void _Function_move_large(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { + _CSTD memcpy(&_Self._Data, &_Src._Data, 2 * sizeof(void*)); // Copy Impl* and indirect data } template -void __stdcall _Function_destroy_small(uintptr_t* _Self) noexcept { - reinterpret_cast<_Fn&>(*_Self).~_Fn(); +void _Function_destroy_small(_Move_only_function_data& _Self) noexcept { + _Self._Small_fn_ptr<_Fn>()->~_Fn(); } template -void __stdcall _Function_destroy_large(uintptr_t* _Self) noexcept { - delete reinterpret_cast<_Fn*>(*_Self); +void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { + delete _Self._Large_fn_ptr<_Fn>(); } template -void __stdcall _Function_move_memcpy(uintptr_t* _Self, uintptr_t* _Src) noexcept { - _CSTD memcpy(_Self, _Src, _Size); +void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { + _CSTD memcpy(&_Self._Data, &_Src._Data, _Size); } template class _Move_only_function_base { public: + using _Impl_t = _Move_only_function_impl<_Rx, _Types...>; + _Move_only_function_data _Data; + _Move_only_function_base() noexcept = default; // leaves fields uninitialized - _Move_only_function_base(_Move_only_function_base&& _Other) noexcept : _Impl(_Other._Impl) { - _Impl->_Move(_Data, _Other._Data); - _Other._Impl = &_Null_move_only_function<_Rx, _Types...>; + _Move_only_function_base(_Move_only_function_base&& _Other) noexcept { + _Other._Get_impl()->_Move(_Data, _Other._Data); + _Other._Data._Impl = &_Null_move_only_function<_Rx, _Types...>; } void _Construct_with_null() noexcept { - _Impl = &_Null_move_only_function<_Rx, _Types...>; + _Data._Impl = &_Null_move_only_function<_Rx, _Types...>; } template void _Construct_with_fn(_Fn&& _Callable) { - _Impl = _Create_impl_ptr<_Fn>(); + _Data._Impl = _Create_impl_ptr<_Fn>(); if constexpr (_Large_function_engaged<_Fn>()) { - reinterpret_cast(_Data[0]) = new _Fn(_STD move(_Callable)); + _Data._Set_large_fn_ptr(new _Fn(_STD move(_Callable))); } else { - new (reinterpret_cast(_Data)) _Fn(_STD move(_Callable)); + ::new (_Data._Buf_ptr<_Fn>()) _Fn(_STD move(_Callable)); } } ~_Move_only_function_base() { - _Impl->_Destroy(_Data); + _Get_impl()->_Destroy(_Data); } - using _Impl_t = _Move_only_function_impl<_Rx, _Types...>; - - alignas(max_align_t) uintptr_t _Data[4]; - const _Impl_t* _Impl; - template static constexpr bool _Large_function_engaged() noexcept { - return sizeof(_Fn) > sizeof(_Data) + return sizeof(_Fn) > _Move_only_function_data::_Buf_size<_Fn>() || !is_nothrow_move_constructible_v<_Fn> || alignof(_Fn) > alignof(max_align_t); } static constexpr size_t _Round_up_size(size_t _Size) { - return size_t{_Size + sizeof(uintptr_t) - 1} & ~size_t{sizeof(uintptr_t) - 1}; + return size_t{_Size + sizeof(void*) - 1} & ~size_t{sizeof(void*) - 1}; + } + + const _Impl_t* _Get_impl() const { + return static_cast(_Data._Impl); } template @@ -1318,19 +1362,20 @@ public: _Impl_t _Impl{}; if constexpr (_Large_function_engaged<_Fn>()) { _Impl._Invoke = _Function_invoke_large<_Fn, _Rx, _Types...>; - _Impl._Move = __std_function_move_large; + _Impl._Move = _Function_move_large; _Impl._Destroy = _Function_destroy_large<_Fn>; } else { _Impl._Invoke = _Function_invoke_small<_Fn, _Rx, _Types...>; if constexpr (is_trivially_copyable_v<_Fn> && is_trivially_destructible_v<_Fn>) { - _Impl._Move = _Function_move_memcpy<_Round_up_size(sizeof(_Fn))>; + _Impl._Move = + _Function_move_memcpy<_Move_only_function_data::_Buf_offset<_Fn>() + _Round_up_size(sizeof(_Fn))>; } else { _Impl._Move = _Function_move_small<_Fn>; } if constexpr (is_trivially_destructible_v<_Fn>) { - _Impl._Destroy = __std_function_noop_destroy; + _Impl._Destroy = _Function_noop_destroy; } else { _Impl._Destroy = _Function_destroy_small<_Fn>; } @@ -1357,7 +1402,7 @@ public: using result_type = _Rx; _Rx operator()(_Types... _Args) { - return this->_Impl->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1368,7 +1413,7 @@ public: using result_type = _Rx; _Rx operator()(_Types... _Args) noexcept { - return this->_Impl->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); } }; #endif diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index 8f4f9433b94..32f06b05871 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -163,7 +163,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(CrtRoot)\github\stl\src\filesystem.cpp; $(CrtRoot)\github\stl\src\format.cpp; $(CrtRoot)\github\stl\src\locale0_implib.cpp; - $(CrtRoot)\github\stl\src\move_only_function.cpp; $(CrtRoot)\github\stl\src\nothrow.cpp; $(CrtRoot)\github\stl\src\sharedmutex.cpp; $(CrtRoot)\github\stl\src\syserror_import_lib.cpp; diff --git a/stl/src/move_only_function.cpp b/stl/src/move_only_function.cpp deleted file mode 100644 index bf118f4b27c..00000000000 --- a/stl/src/move_only_function.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - -// these declarations must be in sync with those in - -extern "C" { - -void __stdcall __std_function_noop_move(uintptr_t*, uintptr_t*) noexcept {} -void __stdcall __std_function_noop_destroy(uintptr_t*) noexcept {} - -void __stdcall __std_function_move_large(uintptr_t* _Self, uintptr_t* _Src) { - *_Self = *_Src; -} -} From 75ef4556c6bcc9b5b11b6bafa233ed488c11d932 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 11 Oct 2021 16:20:44 +0300 Subject: [PATCH 018/112] comments, nullptr check --- stl/inc/functional | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 131510b7fcd..34c0f7bc4a6 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1251,9 +1251,15 @@ union alignas(max_align_t) _Move_only_function_data { }; template -struct _Move_only_function_impl { +struct _Move_only_function_impl { // per-callable-type structure acting as a virtual function table + // using vtable emulations gives more flexibility for optimizations and eliminates the need for RTTI + // (RTTI saving may be significant as with lambdas and binds there may be a lot of distinct callable types) + + // Calls target using _Invoke_t = _Rx (*)(_Move_only_function_data&, Args...); + // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") using _Move_t = void (*)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; + // destroys data (not resetting its "vtable") using _Destroy_t = void (*)(_Move_only_function_data&) _NOEXCEPT_FNPTR; _Invoke_t _Invoke; @@ -1263,7 +1269,7 @@ struct _Move_only_function_impl { template [[noreturn]] _Rx _Function_no_callable(_Move_only_function_data&, Args...) { - _CSTD abort(); + _CSTD abort(); // We are not std::function to throw bad_function_call } constexpr void _Function_noop_move(_Move_only_function_data&, _Move_only_function_data&) noexcept {} @@ -1309,7 +1315,7 @@ void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { template void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { - _CSTD memcpy(&_Self._Data, &_Src._Data, _Size); + _CSTD memcpy(&_Self._Data, &_Src._Data, _Size); // Copy Impl* and functor data } template @@ -1343,17 +1349,21 @@ public: _Get_impl()->_Destroy(_Data); } + bool _Is_null() const noexcept { + return _Data._Impl == &_Null_move_only_function<_Rx, _Types...> + } + template static constexpr bool _Large_function_engaged() noexcept { return sizeof(_Fn) > _Move_only_function_data::_Buf_size<_Fn>() || !is_nothrow_move_constructible_v<_Fn> || alignof(_Fn) > alignof(max_align_t); } - static constexpr size_t _Round_up_size(size_t _Size) { + static constexpr size_t _Round_up_size(size_t _Size) noexcept { return size_t{_Size + sizeof(void*) - 1} & ~size_t{sizeof(void*) - 1}; } - const _Impl_t* _Get_impl() const { + const _Impl_t* _Get_impl() const noexcept { return static_cast(_Data._Impl); } @@ -1439,7 +1449,15 @@ public: this->_Construct_with_fn(_STD forward<_Fn>(_Callable)); } + explicit operator bool() const noexcept { + return !this->_Is_null(); + } + using _Call::operator(); + + friend bool operator==(const move_only_function&, nullptr_t) noexcept { + return this->_Is_null(); + } }; #endif From fc69c3548028cc1dc3153958c1b352d6852304d2 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 11 Oct 2021 16:22:04 +0300 Subject: [PATCH 019/112] null check tests --- .../tests/P0288R9_move_only_function/test.cpp | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 7ef63cd1807..b0d4669b531 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -20,7 +20,6 @@ struct pass_this_by_ref { struct counter { static int inst; - ; counter() { inst++; @@ -61,25 +60,51 @@ struct large_callable : small_callable { char data[large_function_size]; }; +struct odd_cc_callable : counter { + int __fastcall operator()(int a, pass_this_by_ref& b) { + assert(a == 23); + assert(b.v == 63); + return 38; + } + + odd_cc_callable() = default; + + odd_cc_callable(const odd_cc_callable&) { + abort(); + } + + odd_cc_callable(odd_cc_callable&&) noexcept = default; +}; + using test_function_t = move_only_function; -template -void test_impl() { +template +void test_impl(Args... args) { { pass_this_by_ref x{63}; - test_function_t f1(F{}); + test_function_t f1(F{args...}); assert(f1(23, x) == 38); + assert(f1); + assert(f1 != nullptr); + test_function_t f2 = std::move(f1); assert(f2(23, x) == 38); + + assert(!f1); + assert(f1 == nullptr); } + test_function_t f3; + assert(!f3); + assert(f3 == nullptr); + assert(counter::inst == 0); } - int main() { test_impl(); test_impl(); + test_impl(); } From 610a5a751f4ed6104f61b1871cf566a36cae33c4 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 11 Oct 2021 16:29:58 +0300 Subject: [PATCH 020/112] clang format --- stl/inc/functional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 34c0f7bc4a6..e789c30f53c 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1256,9 +1256,9 @@ struct _Move_only_function_impl { // per-callable-type structure acting as a vir // (RTTI saving may be significant as with lambdas and binds there may be a lot of distinct callable types) // Calls target - using _Invoke_t = _Rx (*)(_Move_only_function_data&, Args...); + using _Invoke_t = _Rx (*)(_Move_only_function_data&, Args...); // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") - using _Move_t = void (*)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; + using _Move_t = void (*)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; // destroys data (not resetting its "vtable") using _Destroy_t = void (*)(_Move_only_function_data&) _NOEXCEPT_FNPTR; From 1bc62c81b84d985fe82f18564fbc3faae2f3d057 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 11 Oct 2021 16:34:56 +0300 Subject: [PATCH 021/112] fix null test --- stl/inc/functional | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index e789c30f53c..76bd8bff8b4 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1350,7 +1350,7 @@ public: } bool _Is_null() const noexcept { - return _Data._Impl == &_Null_move_only_function<_Rx, _Types...> + return _Data._Impl == &_Null_move_only_function<_Rx, _Types...>; } template @@ -1455,8 +1455,8 @@ public: using _Call::operator(); - friend bool operator==(const move_only_function&, nullptr_t) noexcept { - return this->_Is_null(); + friend bool operator==(const move_only_function& mf, nullptr_t) noexcept { + return mf._Is_null(); } }; #endif From 498c47f45aaf837905be81f76f0209d17d00a187 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 11 Oct 2021 16:35:42 +0300 Subject: [PATCH 022/112] naming --- stl/inc/functional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 76bd8bff8b4..268e09f8a6a 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1455,8 +1455,8 @@ public: using _Call::operator(); - friend bool operator==(const move_only_function& mf, nullptr_t) noexcept { - return mf._Is_null(); + friend bool operator==(const move_only_function& _This, nullptr_t) noexcept { + return _This._Is_null(); } }; #endif From 7abfdbbdc1579e283b70c411ac2c86a8125a38f4 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 11 Oct 2021 20:16:07 +0300 Subject: [PATCH 023/112] global new delete --- stl/inc/functional | 7 ++++--- tests/std/tests/P0288R9_move_only_function/test.cpp | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 268e09f8a6a..b2cc1b6c281 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1215,7 +1215,6 @@ union alignas(max_align_t) _Move_only_function_data { const void* _Impl; char _Data; // For aliasing - template static constexpr size_t _Buf_offset() noexcept { if constexpr (alignof(_Fn) <= sizeof(_Impl)) { @@ -1310,7 +1309,9 @@ void _Function_destroy_small(_Move_only_function_data& _Self) noexcept { template void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { - delete _Self._Large_fn_ptr<_Fn>(); + _Fn* _Pfn = _Self._Large_fn_ptr<_Fn>(); + _Pfn->~_Fn(); + _Deallocate(_Pfn, sizeof(_Fn)); } template @@ -1339,7 +1340,7 @@ public: void _Construct_with_fn(_Fn&& _Callable) { _Data._Impl = _Create_impl_ptr<_Fn>(); if constexpr (_Large_function_engaged<_Fn>()) { - _Data._Set_large_fn_ptr(new _Fn(_STD move(_Callable))); + _Data._Set_large_fn_ptr(_Global_new<_Fn>(_STD move(_Callable))); } else { ::new (_Data._Buf_ptr<_Fn>()) _Fn(_STD move(_Callable)); } diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index b0d4669b531..010e9d84b49 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -47,6 +47,9 @@ struct small_callable : counter { return 38; } + void* operator new(size_t) = delete; + void operator delete(void*) = delete; + small_callable() = default; small_callable(const small_callable&) { From f2cac3614af17794afeeb695c27d1388e0bb3815 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 11 Oct 2021 20:25:40 +0300 Subject: [PATCH 024/112] clang format --- tests/std/tests/P0288R9_move_only_function/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 010e9d84b49..8f32be855da 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -47,7 +47,7 @@ struct small_callable : counter { return 38; } - void* operator new(size_t) = delete; + void* operator new(size_t) = delete; void operator delete(void*) = delete; small_callable() = default; From b20cb6fff112b891ac99f20125b20f6ea8e8e630 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 12 Oct 2021 15:24:02 +0300 Subject: [PATCH 025/112] large function allocation simplification --- stl/inc/functional | 71 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index b2cc1b6c281..f184612712d 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1255,15 +1255,11 @@ struct _Move_only_function_impl { // per-callable-type structure acting as a vir // (RTTI saving may be significant as with lambdas and binds there may be a lot of distinct callable types) // Calls target - using _Invoke_t = _Rx (*)(_Move_only_function_data&, Args...); + _Rx (*_Invoke)(_Move_only_function_data&, Args...); // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") - using _Move_t = void (*)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; + void (*_Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; // destroys data (not resetting its "vtable") - using _Destroy_t = void (*)(_Move_only_function_data&) _NOEXCEPT_FNPTR; - - _Invoke_t _Invoke; - _Move_t _Move; - _Destroy_t _Destroy; + void (*_Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR; }; template @@ -1307,11 +1303,23 @@ void _Function_destroy_small(_Move_only_function_data& _Self) noexcept { _Self._Small_fn_ptr<_Fn>()->~_Fn(); } +inline void _Function_deallocate_large_default_aligned(_Move_only_function_data& _Self) noexcept { + ::operator delete(_Self._Large_fn_ptr()); +} + +template +void _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noexcept { + ::operator delete (_Self._Large_fn_ptr(), align_val_t{_Align}); +} + template void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { - _Fn* _Pfn = _Self._Large_fn_ptr<_Fn>(); - _Pfn->~_Fn(); - _Deallocate(_Pfn, sizeof(_Fn)); + _Self._Large_fn_ptr<_Fn>()->~_Fn(); + if constexpr (alignof(_Fn) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + _Function_deallocate_large_default_aligned(_Self); + } else { + _Function_deallocate_large_overaligned(_Self); + } } template @@ -1319,6 +1327,32 @@ void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_ _CSTD memcpy(&_Self._Data, &_Src._Data, _Size); // Copy Impl* and functor data } +template +void* _Function_new_large(_Fn&& _Callable) { + struct [[nodiscard]] _Guard_type { + void* _Ptr; + + ~_Guard_type() { + if (_Ptr) { + if constexpr (alignof(_Fn) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + ::operator delete(_Ptr); + } else { + ::operator delete (_Ptr, align_val_t{alignof(_Fn)}); + } + } + } + }; + + _Guard_type _Guard; + if constexpr (alignof(_Fn) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + _Guard._Ptr = ::operator new(sizeof(_Fn)); + } else { + _Guard._Ptr = ::operator new(sizeof(_Fn), align_val_t{alignof(_Fn)}); + } + ::new (_Guard._Ptr) _Fn(_STD move(_Callable)); + return exchange(_Guard._Ptr, nullptr); +} + template class _Move_only_function_base { public: @@ -1340,7 +1374,7 @@ public: void _Construct_with_fn(_Fn&& _Callable) { _Data._Impl = _Create_impl_ptr<_Fn>(); if constexpr (_Large_function_engaged<_Fn>()) { - _Data._Set_large_fn_ptr(_Global_new<_Fn>(_STD move(_Callable))); + _Data._Set_large_fn_ptr(_Function_new_large<_Fn>(_STD move(_Callable))); } else { ::new (_Data._Buf_ptr<_Fn>()) _Fn(_STD move(_Callable)); } @@ -1372,9 +1406,18 @@ public: static constexpr _Impl_t _Create_impl() noexcept { _Impl_t _Impl{}; if constexpr (_Large_function_engaged<_Fn>()) { - _Impl._Invoke = _Function_invoke_large<_Fn, _Rx, _Types...>; - _Impl._Move = _Function_move_large; - _Impl._Destroy = _Function_destroy_large<_Fn>; + _Impl._Invoke = _Function_invoke_large<_Fn, _Rx, _Types...>; + _Impl._Move = _Function_move_large; + + if constexpr (is_trivially_destructible_v<_Fn>) { + if constexpr (alignof(_Fn) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + _Impl._Destroy = _Function_deallocate_large_default_aligned; + } else { + _Impl._Destroy = _Function_deallocate_large_overaligned; + } + } else { + _Impl._Destroy = _Function_destroy_large<_Fn>; + } } else { _Impl._Invoke = _Function_invoke_small<_Fn, _Rx, _Types...>; From 6d5f2aed0ed0b1989be23cd1a12572fd9a320c36 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 12 Oct 2021 15:51:45 +0300 Subject: [PATCH 026/112] spam nodiscard and noexcept --- stl/inc/functional | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index f184612712d..a55e23d6b39 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1216,7 +1216,7 @@ union alignas(max_align_t) _Move_only_function_data { char _Data; // For aliasing template - static constexpr size_t _Buf_offset() noexcept { + [[nodiscard]] static constexpr size_t _Buf_offset() noexcept { if constexpr (alignof(_Fn) <= sizeof(_Impl)) { return sizeof(_Impl); // Data immediately after impl } else { @@ -1225,34 +1225,35 @@ union alignas(max_align_t) _Move_only_function_data { } template - static constexpr size_t _Buf_size() noexcept { + [[nodiscard]] static constexpr size_t _Buf_size() noexcept { return sizeof(_Pointers) - _Buf_offset<_Fn>(); } template - constexpr void* _Buf_ptr() { + [[nodiscard]] constexpr void* _Buf_ptr() noexcept { return &_Data + _Buf_offset<_Fn>(); } template - _Fn* _Small_fn_ptr() { + [[nodiscard]] _Fn* _Small_fn_ptr() noexcept { return static_cast<_Fn*>(_Buf_ptr<_Fn>()); } template - _Fn* _Large_fn_ptr() { + [[nodiscard]] _Fn* _Large_fn_ptr() noexcept { return static_cast<_Fn*>(_Pointers[1]); } - void _Set_large_fn_ptr(void* _Value) { + void _Set_large_fn_ptr(void* const _Value) noexcept { _Pointers[1] = _Value; } }; template struct _Move_only_function_impl { // per-callable-type structure acting as a virtual function table - // using vtable emulations gives more flexibility for optimizations and eliminates the need for RTTI - // (RTTI saving may be significant as with lambdas and binds there may be a lot of distinct callable types) + // using vtable emulations gives more flexibility for optimizations and reduces the need for RTTIs + // (RTTI saving may be significant as with lambdas and binds there may be a lot of distinct callable types, + // we don't have distinct wrapper class type for each callable type, only distinct functions when needed) // Calls target _Rx (*_Invoke)(_Move_only_function_data&, Args...); @@ -1263,12 +1264,12 @@ struct _Move_only_function_impl { // per-callable-type structure acting as a vir }; template -[[noreturn]] _Rx _Function_no_callable(_Move_only_function_data&, Args...) { +[[noreturn]] _Rx _Function_no_callable(_Move_only_function_data&, Args...) noexcept { _CSTD abort(); // We are not std::function to throw bad_function_call } -constexpr void _Function_noop_move(_Move_only_function_data&, _Move_only_function_data&) noexcept {} -constexpr void _Function_noop_destroy(_Move_only_function_data&) noexcept {} +inline void _Function_noop_move(_Move_only_function_data&, _Move_only_function_data&) noexcept {} +inline void _Function_noop_destroy(_Move_only_function_data&) noexcept {} template inline constexpr _Move_only_function_impl<_Rx, Args...> _Null_move_only_function = { @@ -1278,12 +1279,12 @@ inline constexpr _Move_only_function_impl<_Rx, Args...> _Null_move_only_function }; template -_Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types... _Args) { +[[nodiscard]] _Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types... _Args) { return (*_Self._Small_fn_ptr<_Fn>()) (_STD forward<_Types>(_Args)...); } template -_Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types... _Args) { +[[nodiscard]] _Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types... _Args) { return (*_Self._Large_fn_ptr<_Fn>()) (_STD forward<_Types>(_Args)...); } @@ -1328,7 +1329,7 @@ void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_ } template -void* _Function_new_large(_Fn&& _Callable) { +[[nodiscard]] void* _Function_new_large(_Fn&& _Callable) { struct [[nodiscard]] _Guard_type { void* _Ptr; @@ -1347,7 +1348,7 @@ void* _Function_new_large(_Fn&& _Callable) { if constexpr (alignof(_Fn) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { _Guard._Ptr = ::operator new(sizeof(_Fn)); } else { - _Guard._Ptr = ::operator new(sizeof(_Fn), align_val_t{alignof(_Fn)}); + _Guard._Ptr = ::operator new (sizeof(_Fn), align_val_t{alignof(_Fn)}); } ::new (_Guard._Ptr) _Fn(_STD move(_Callable)); return exchange(_Guard._Ptr, nullptr); @@ -1384,12 +1385,12 @@ public: _Get_impl()->_Destroy(_Data); } - bool _Is_null() const noexcept { + [[nodiscard]] bool _Is_null() const noexcept { return _Data._Impl == &_Null_move_only_function<_Rx, _Types...>; } template - static constexpr bool _Large_function_engaged() noexcept { + [[nodiscard]] static constexpr bool _Large_function_engaged() noexcept { return sizeof(_Fn) > _Move_only_function_data::_Buf_size<_Fn>() || !is_nothrow_move_constructible_v<_Fn> || alignof(_Fn) > alignof(max_align_t); } @@ -1398,12 +1399,12 @@ public: return size_t{_Size + sizeof(void*) - 1} & ~size_t{sizeof(void*) - 1}; } - const _Impl_t* _Get_impl() const noexcept { + [[nodiscard]] const _Impl_t* _Get_impl() const noexcept { return static_cast(_Data._Impl); } template - static constexpr _Impl_t _Create_impl() noexcept { + [[nodiscard]] static constexpr _Impl_t _Create_impl() noexcept { _Impl_t _Impl{}; if constexpr (_Large_function_engaged<_Fn>()) { _Impl._Invoke = _Function_invoke_large<_Fn, _Rx, _Types...>; @@ -1438,7 +1439,7 @@ public: } template - static const _Impl_t* _Create_impl_ptr() noexcept { + [[nodiscard]] static const _Impl_t* _Create_impl_ptr() noexcept { static constexpr _Impl_t _Impl = _Create_impl<_Fn>(); return &_Impl; } @@ -1493,13 +1494,13 @@ public: this->_Construct_with_fn(_STD forward<_Fn>(_Callable)); } - explicit operator bool() const noexcept { + [[nodiscard]] explicit operator bool() const noexcept { return !this->_Is_null(); } using _Call::operator(); - friend bool operator==(const move_only_function& _This, nullptr_t) noexcept { + [[nodiscard]] friend bool operator==(const move_only_function& _This, nullptr_t) noexcept { return _This._Is_null(); } }; From e5a04d356cb1f6cfaeebd3eeb56efe73f4838515 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 12 Oct 2021 16:06:23 +0300 Subject: [PATCH 027/112] _NODISCARD --- stl/inc/functional | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index a55e23d6b39..af77aba0cf9 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1216,7 +1216,7 @@ union alignas(max_align_t) _Move_only_function_data { char _Data; // For aliasing template - [[nodiscard]] static constexpr size_t _Buf_offset() noexcept { + _NODISCARD static constexpr size_t _Buf_offset() noexcept { if constexpr (alignof(_Fn) <= sizeof(_Impl)) { return sizeof(_Impl); // Data immediately after impl } else { @@ -1225,22 +1225,22 @@ union alignas(max_align_t) _Move_only_function_data { } template - [[nodiscard]] static constexpr size_t _Buf_size() noexcept { + _NODISCARD static constexpr size_t _Buf_size() noexcept { return sizeof(_Pointers) - _Buf_offset<_Fn>(); } template - [[nodiscard]] constexpr void* _Buf_ptr() noexcept { + _NODISCARD constexpr void* _Buf_ptr() noexcept { return &_Data + _Buf_offset<_Fn>(); } template - [[nodiscard]] _Fn* _Small_fn_ptr() noexcept { + _NODISCARD _Fn* _Small_fn_ptr() noexcept { return static_cast<_Fn*>(_Buf_ptr<_Fn>()); } template - [[nodiscard]] _Fn* _Large_fn_ptr() noexcept { + _NODISCARD _Fn* _Large_fn_ptr() noexcept { return static_cast<_Fn*>(_Pointers[1]); } @@ -1279,12 +1279,12 @@ inline constexpr _Move_only_function_impl<_Rx, Args...> _Null_move_only_function }; template -[[nodiscard]] _Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types... _Args) { +_NODISCARD _Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types... _Args) { return (*_Self._Small_fn_ptr<_Fn>()) (_STD forward<_Types>(_Args)...); } template -[[nodiscard]] _Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types... _Args) { +_NODISCARD _Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types... _Args) { return (*_Self._Large_fn_ptr<_Fn>()) (_STD forward<_Types>(_Args)...); } @@ -1329,8 +1329,8 @@ void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_ } template -[[nodiscard]] void* _Function_new_large(_Fn&& _Callable) { - struct [[nodiscard]] _Guard_type { +_NODISCARD void* _Function_new_large(_Fn&& _Callable) { + struct _NODISCARD _Guard_type { void* _Ptr; ~_Guard_type() { @@ -1385,12 +1385,12 @@ public: _Get_impl()->_Destroy(_Data); } - [[nodiscard]] bool _Is_null() const noexcept { + _NODISCARD bool _Is_null() const noexcept { return _Data._Impl == &_Null_move_only_function<_Rx, _Types...>; } template - [[nodiscard]] static constexpr bool _Large_function_engaged() noexcept { + _NODISCARD static constexpr bool _Large_function_engaged() noexcept { return sizeof(_Fn) > _Move_only_function_data::_Buf_size<_Fn>() || !is_nothrow_move_constructible_v<_Fn> || alignof(_Fn) > alignof(max_align_t); } @@ -1399,12 +1399,12 @@ public: return size_t{_Size + sizeof(void*) - 1} & ~size_t{sizeof(void*) - 1}; } - [[nodiscard]] const _Impl_t* _Get_impl() const noexcept { + _NODISCARD const _Impl_t* _Get_impl() const noexcept { return static_cast(_Data._Impl); } template - [[nodiscard]] static constexpr _Impl_t _Create_impl() noexcept { + _NODISCARD static constexpr _Impl_t _Create_impl() noexcept { _Impl_t _Impl{}; if constexpr (_Large_function_engaged<_Fn>()) { _Impl._Invoke = _Function_invoke_large<_Fn, _Rx, _Types...>; @@ -1439,7 +1439,7 @@ public: } template - [[nodiscard]] static const _Impl_t* _Create_impl_ptr() noexcept { + _NODISCARD static const _Impl_t* _Create_impl_ptr() noexcept { static constexpr _Impl_t _Impl = _Create_impl<_Fn>(); return &_Impl; } @@ -1494,13 +1494,13 @@ public: this->_Construct_with_fn(_STD forward<_Fn>(_Callable)); } - [[nodiscard]] explicit operator bool() const noexcept { + _NODISCARD explicit operator bool() const noexcept { return !this->_Is_null(); } using _Call::operator(); - [[nodiscard]] friend bool operator==(const move_only_function& _This, nullptr_t) noexcept { + _NODISCARD friend bool operator==(const move_only_function& _This, nullptr_t) noexcept { return _This._Is_null(); } }; From 9d3b2d78e4c8d303650d352ca4b1ddfbd08e644a Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 12 Oct 2021 17:33:21 +0300 Subject: [PATCH 028/112] _STD --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index af77aba0cf9..db5a127430a 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1351,7 +1351,7 @@ _NODISCARD void* _Function_new_large(_Fn&& _Callable) { _Guard._Ptr = ::operator new (sizeof(_Fn), align_val_t{alignof(_Fn)}); } ::new (_Guard._Ptr) _Fn(_STD move(_Callable)); - return exchange(_Guard._Ptr, nullptr); + return _STD exchange(_Guard._Ptr, nullptr); } template From 785354f2cc27431fe6400853ebb10642d3eee479 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 13 Oct 2021 09:15:50 +0300 Subject: [PATCH 029/112] perfect forwarding --- stl/inc/functional | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index db5a127430a..ad75a95761c 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1249,42 +1249,42 @@ union alignas(max_align_t) _Move_only_function_data { } }; -template +template struct _Move_only_function_impl { // per-callable-type structure acting as a virtual function table // using vtable emulations gives more flexibility for optimizations and reduces the need for RTTIs // (RTTI saving may be significant as with lambdas and binds there may be a lot of distinct callable types, // we don't have distinct wrapper class type for each callable type, only distinct functions when needed) // Calls target - _Rx (*_Invoke)(_Move_only_function_data&, Args...); + _Rx (*_Invoke)(_Move_only_function_data&, _Types&&...); // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") void (*_Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; // destroys data (not resetting its "vtable") void (*_Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR; }; -template -[[noreturn]] _Rx _Function_no_callable(_Move_only_function_data&, Args...) noexcept { +template +[[noreturn]] _Rx _Function_no_callable(_Move_only_function_data&, _Types&&...) noexcept { _CSTD abort(); // We are not std::function to throw bad_function_call } inline void _Function_noop_move(_Move_only_function_data&, _Move_only_function_data&) noexcept {} inline void _Function_noop_destroy(_Move_only_function_data&) noexcept {} -template -inline constexpr _Move_only_function_impl<_Rx, Args...> _Null_move_only_function = { - _Function_no_callable<_Rx, Args...>, +template +inline constexpr _Move_only_function_impl<_Rx, _Types...> _Null_move_only_function = { + _Function_no_callable<_Rx, _Types...>, _Function_noop_move, _Function_noop_destroy, }; template -_NODISCARD _Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types... _Args) { +_NODISCARD _Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types&&... _Args) { return (*_Self._Small_fn_ptr<_Fn>()) (_STD forward<_Types>(_Args)...); } template -_NODISCARD _Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types... _Args) { +_NODISCARD _Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types&&... _Args) { return (*_Self._Large_fn_ptr<_Fn>()) (_STD forward<_Types>(_Args)...); } @@ -1456,7 +1456,7 @@ class _Move_only_function_call<_Rx(_Types...)> : public _Move_only_function_base public: using result_type = _Rx; - _Rx operator()(_Types... _Args) { + _Rx operator()(_Types&&... _Args) { return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1467,7 +1467,7 @@ class _Move_only_function_call<_Rx(_Types...) noexcept> : public _Move_only_func public: using result_type = _Rx; - _Rx operator()(_Types... _Args) noexcept { + _Rx operator()(_Types&&... _Args) noexcept { return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); } }; From bf8a29590a96f833d9a37ac4e72bab8f9d403e9b Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 13 Oct 2021 16:18:22 +0300 Subject: [PATCH 030/112] empty move still moves vtable --- stl/inc/functional | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index ad75a95761c..0f489f9de06 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1268,14 +1268,17 @@ template _CSTD abort(); // We are not std::function to throw bad_function_call } -inline void _Function_noop_move(_Move_only_function_data&, _Move_only_function_data&) noexcept {} -inline void _Function_noop_destroy(_Move_only_function_data&) noexcept {} +inline void _Function_empty_move(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { + _Self._Impl = _Src._Impl; // Function data is empty, still need to copy "vtable" +} + +inline void _Function_empty_destroy(_Move_only_function_data&) noexcept {} template inline constexpr _Move_only_function_impl<_Rx, _Types...> _Null_move_only_function = { _Function_no_callable<_Rx, _Types...>, - _Function_noop_move, - _Function_noop_destroy, + _Function_empty_move, + _Function_empty_destroy, }; template @@ -1430,7 +1433,7 @@ public: } if constexpr (is_trivially_destructible_v<_Fn>) { - _Impl._Destroy = _Function_noop_destroy; + _Impl._Destroy = _Function_empty_destroy; } else { _Impl._Destroy = _Function_destroy_small<_Fn>; } From 6222cad74f26152f54c6c55c630486806043593b Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 13 Oct 2021 17:26:22 +0300 Subject: [PATCH 031/112] null check --- stl/inc/functional | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stl/inc/functional b/stl/inc/functional index 0f489f9de06..e79449e7345 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1494,6 +1494,13 @@ public: template move_only_function(_Fn&& _Callable) { + using _Vt = decay_t<_Fn>; + if constexpr (is_member_function_pointer_v<_Vt> || is_member_object_pointer_v<_Vt> || is_pointer_v<_Vt>) { + if (_Callable == nullptr) { + this->_Construct_with_null(); + return; + } + } this->_Construct_with_fn(_STD forward<_Fn>(_Callable)); } From 04cfef1059e79089d21f1f6c65361c48397d63f7 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 13 Oct 2021 22:46:55 +0300 Subject: [PATCH 032/112] right decay semantic --- stl/inc/functional | 95 ++++++++++--------- .../tests/P0288R9_move_only_function/test.cpp | 77 ++++++++++----- 2 files changed, 103 insertions(+), 69 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index e79449e7345..c1c57590ad9 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1281,20 +1281,20 @@ inline constexpr _Move_only_function_impl<_Rx, _Types...> _Null_move_only_functi _Function_empty_destroy, }; -template +template _NODISCARD _Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types&&... _Args) { - return (*_Self._Small_fn_ptr<_Fn>()) (_STD forward<_Types>(_Args)...); + return _Invoker_ret<_Rx>::_Call(*_Self._Small_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } -template +template _NODISCARD _Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types&&... _Args) { - return (*_Self._Large_fn_ptr<_Fn>()) (_STD forward<_Types>(_Args)...); + return _Invoker_ret<_Rx>::_Call(*_Self._Large_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } -template +template void _Function_move_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { - ::new (_Self._Buf_ptr<_Fn>()) _Fn(_STD move(*_Src._Small_fn_ptr<_Fn>())); - _Src._Small_fn_ptr<_Fn>()->~_Fn(); + ::new (_Self._Buf_ptr<_Vt>()) _Vt(_STD move(*_Src._Small_fn_ptr<_Vt>())); + _Src._Small_fn_ptr<_Vt>()->~_Vt(); _Self._Impl = _Src._Impl; } @@ -1302,9 +1302,9 @@ inline void _Function_move_large(_Move_only_function_data& _Self, _Move_only_fun _CSTD memcpy(&_Self._Data, &_Src._Data, 2 * sizeof(void*)); // Copy Impl* and indirect data } -template +template void _Function_destroy_small(_Move_only_function_data& _Self) noexcept { - _Self._Small_fn_ptr<_Fn>()->~_Fn(); + _Self._Small_fn_ptr<_Vt>()->~_Vt(); } inline void _Function_deallocate_large_default_aligned(_Move_only_function_data& _Self) noexcept { @@ -1316,13 +1316,13 @@ void _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noe ::operator delete (_Self._Large_fn_ptr(), align_val_t{_Align}); } -template +template void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { - _Self._Large_fn_ptr<_Fn>()->~_Fn(); - if constexpr (alignof(_Fn) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + _Self._Large_fn_ptr<_Vt>()->~_Vt(); + if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { _Function_deallocate_large_default_aligned(_Self); } else { - _Function_deallocate_large_overaligned(_Self); + _Function_deallocate_large_overaligned(_Self); } } @@ -1331,29 +1331,29 @@ void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_ _CSTD memcpy(&_Self._Data, &_Src._Data, _Size); // Copy Impl* and functor data } -template -_NODISCARD void* _Function_new_large(_Fn&& _Callable) { +template +_NODISCARD void* _Function_new_large(_Types&&... _Args) { struct _NODISCARD _Guard_type { void* _Ptr; ~_Guard_type() { if (_Ptr) { - if constexpr (alignof(_Fn) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { ::operator delete(_Ptr); } else { - ::operator delete (_Ptr, align_val_t{alignof(_Fn)}); + ::operator delete (_Ptr, align_val_t{alignof(_Vt)}); } } } }; _Guard_type _Guard; - if constexpr (alignof(_Fn) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { - _Guard._Ptr = ::operator new(sizeof(_Fn)); + if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + _Guard._Ptr = ::operator new(sizeof(_Vt)); } else { - _Guard._Ptr = ::operator new (sizeof(_Fn), align_val_t{alignof(_Fn)}); + _Guard._Ptr = ::operator new (sizeof(_Vt), align_val_t{alignof(_Vt)}); } - ::new (_Guard._Ptr) _Fn(_STD move(_Callable)); + ::new (_Guard._Ptr) _Vt(_STD forward<_Types>(_Args)...); return _STD exchange(_Guard._Ptr, nullptr); } @@ -1374,13 +1374,13 @@ public: _Data._Impl = &_Null_move_only_function<_Rx, _Types...>; } - template - void _Construct_with_fn(_Fn&& _Callable) { - _Data._Impl = _Create_impl_ptr<_Fn>(); - if constexpr (_Large_function_engaged<_Fn>()) { - _Data._Set_large_fn_ptr(_Function_new_large<_Fn>(_STD move(_Callable))); + template + void _Construct_with_fn(_Types&&... _Args) { + _Data._Impl = _Create_impl_ptr<_Vt>(); + if constexpr (_Large_function_engaged<_Vt>()) { + _Data._Set_large_fn_ptr(_Function_new_large<_Vt>(_STD forward<_Types>(_Args)...)); } else { - ::new (_Data._Buf_ptr<_Fn>()) _Fn(_STD move(_Callable)); + ::new (_Data._Buf_ptr<_Vt>()) _Vt(_STD forward<_Types>(_Args)...); } } @@ -1392,10 +1392,10 @@ public: return _Data._Impl == &_Null_move_only_function<_Rx, _Types...>; } - template + template _NODISCARD static constexpr bool _Large_function_engaged() noexcept { - return sizeof(_Fn) > _Move_only_function_data::_Buf_size<_Fn>() - || !is_nothrow_move_constructible_v<_Fn> || alignof(_Fn) > alignof(max_align_t); + return sizeof(_Vt) > _Move_only_function_data::_Buf_size<_Vt>() + || !is_nothrow_move_constructible_v<_Vt> || alignof(_Vt) > alignof(max_align_t); } static constexpr size_t _Round_up_size(size_t _Size) noexcept { @@ -1406,44 +1406,44 @@ public: return static_cast(_Data._Impl); } - template + template _NODISCARD static constexpr _Impl_t _Create_impl() noexcept { _Impl_t _Impl{}; - if constexpr (_Large_function_engaged<_Fn>()) { - _Impl._Invoke = _Function_invoke_large<_Fn, _Rx, _Types...>; + if constexpr (_Large_function_engaged<_Vt>()) { + _Impl._Invoke = _Function_invoke_large<_Vt, _Rx, _Types...>; _Impl._Move = _Function_move_large; - if constexpr (is_trivially_destructible_v<_Fn>) { - if constexpr (alignof(_Fn) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { + 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; + _Impl._Destroy = _Function_deallocate_large_overaligned; } } else { - _Impl._Destroy = _Function_destroy_large<_Fn>; + _Impl._Destroy = _Function_destroy_large<_Vt>; } } else { - _Impl._Invoke = _Function_invoke_small<_Fn, _Rx, _Types...>; + _Impl._Invoke = _Function_invoke_small<_Vt, _Rx, _Types...>; - if constexpr (is_trivially_copyable_v<_Fn> && is_trivially_destructible_v<_Fn>) { + if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) { _Impl._Move = - _Function_move_memcpy<_Move_only_function_data::_Buf_offset<_Fn>() + _Round_up_size(sizeof(_Fn))>; + _Function_move_memcpy<_Move_only_function_data::_Buf_offset<_Vt>() + _Round_up_size(sizeof(_Vt))>; } else { - _Impl._Move = _Function_move_small<_Fn>; + _Impl._Move = _Function_move_small<_Vt>; } - if constexpr (is_trivially_destructible_v<_Fn>) { + if constexpr (is_trivially_destructible_v<_Vt>) { _Impl._Destroy = _Function_empty_destroy; } else { - _Impl._Destroy = _Function_destroy_small<_Fn>; + _Impl._Destroy = _Function_destroy_small<_Vt>; } } return _Impl; } - template + template _NODISCARD static const _Impl_t* _Create_impl_ptr() noexcept { - static constexpr _Impl_t _Impl = _Create_impl<_Fn>(); + static constexpr _Impl_t _Impl = _Create_impl<_Vt>(); return &_Impl; } }; @@ -1495,13 +1495,16 @@ public: template move_only_function(_Fn&& _Callable) { using _Vt = decay_t<_Fn>; + static_assert(is_constructible_v<_Vt, _Fn>); + if constexpr (is_member_function_pointer_v<_Vt> || is_member_object_pointer_v<_Vt> || is_pointer_v<_Vt>) { if (_Callable == nullptr) { this->_Construct_with_null(); return; } } - this->_Construct_with_fn(_STD forward<_Fn>(_Callable)); + + this->_Construct_with_fn<_Vt>(_STD forward<_Fn>(_Callable)); } _NODISCARD explicit operator bool() const noexcept { diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 8f32be855da..4ba95ae4b80 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -20,25 +20,31 @@ struct pass_this_by_ref { struct counter { static int inst; + static int copies; + static int moves; counter() { - inst++; + ++inst; } counter(const counter&) { - inst++; + ++inst; + ++copies; } counter(counter&&) noexcept { - inst++; + ++inst; + ++moves; } ~counter() { - inst--; + --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) { @@ -52,9 +58,7 @@ struct small_callable : counter { small_callable() = default; - small_callable(const small_callable&) { - abort(); - } + small_callable(const small_callable&) = default; small_callable(small_callable&&) noexcept = default; }; @@ -72,13 +76,16 @@ struct odd_cc_callable : counter { odd_cc_callable() = default; - odd_cc_callable(const odd_cc_callable&) { - abort(); - } - + odd_cc_callable(const odd_cc_callable&) = default; odd_cc_callable(odd_cc_callable&&) noexcept = default; }; +int __fastcall plain_callable(int a, pass_this_by_ref& b) { + assert(a == 23); + assert(b.v == 63); + return 38; +} + using test_function_t = move_only_function; template @@ -86,28 +93,52 @@ void test_impl(Args... args) { { pass_this_by_ref x{63}; - test_function_t f1(F{args...}); - assert(f1(23, x) == 38); + test_function_t constructed_directly(F{args...}); - assert(f1); - assert(f1 != nullptr); + assert(constructed_directly(23, x) == 38); - test_function_t f2 = std::move(f1); + assert(constructed_directly); + assert(constructed_directly != nullptr); - assert(f2(23, x) == 38); + test_function_t move_constructed = std::move(constructed_directly); - assert(!f1); - assert(f1 == nullptr); + assert(move_constructed(23, x) == 38); + + assert(!constructed_directly); + assert(constructed_directly == nullptr); + + if constexpr (is_class_v) { + assert(counter::copies == 0); + } + + F v{args...}; + test_function_t constructed_lvalue(v); + if constexpr (is_class_v) { + assert(counter::copies == 1); + counter::copies = 0; + } } - test_function_t f3; - assert(!f3); - assert(f3 == nullptr); - assert(counter::inst == 0); + if constexpr (is_class_v) { + assert(counter::inst == 0); + } +} + +void test_empty() { + test_function_t emtpty; + assert(!emtpty); + assert(emtpty == nullptr); + + test_function_t emtpty_moved = std::move(emtpty); + assert(!emtpty_moved); + assert(emtpty_moved == nullptr); + assert(!emtpty_moved); + assert(emtpty_moved == nullptr); } int main() { test_impl(); test_impl(); test_impl(); + test_impl(plain_callable); } From efe890027a37503c7e1fb059fe20aa3d1e7a677e Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 13 Oct 2021 22:55:42 +0300 Subject: [PATCH 033/112] clang format --- tests/std/tests/P0288R9_move_only_function/test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 4ba95ae4b80..1316c263826 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -42,9 +42,9 @@ struct counter { } }; -int counter::inst = 0; +int counter::inst = 0; int counter::copies = 0; -int counter::moves = 0; +int counter::moves = 0; struct small_callable : counter { int operator()(int a, pass_this_by_ref& b) { @@ -76,7 +76,7 @@ struct odd_cc_callable : counter { odd_cc_callable() = default; - odd_cc_callable(const odd_cc_callable&) = default; + odd_cc_callable(const odd_cc_callable&) = default; odd_cc_callable(odd_cc_callable&&) noexcept = default; }; From 7fa8408eebe1987744a5921d12cf48faf1a9c285 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 12:38:41 +0300 Subject: [PATCH 034/112] in-place construction --- stl/inc/functional | 67 ++++++++++++++----- .../tests/P0288R9_move_only_function/test.cpp | 10 +++ 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index c1c57590ad9..fd47064b47c 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1281,14 +1281,14 @@ inline constexpr _Move_only_function_impl<_Rx, _Types...> _Null_move_only_functi _Function_empty_destroy, }; -template +template _NODISCARD _Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types&&... _Args) { - return _Invoker_ret<_Rx>::_Call(*_Self._Small_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); + return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } -template +template _NODISCARD _Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types&&... _Args) { - return _Invoker_ret<_Rx>::_Call(*_Self._Large_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); + return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } template @@ -1374,13 +1374,13 @@ public: _Data._Impl = &_Null_move_only_function<_Rx, _Types...>; } - template - void _Construct_with_fn(_Types&&... _Args) { - _Data._Impl = _Create_impl_ptr<_Vt>(); + template + 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<_Types>(_Args)...)); + _Data._Set_large_fn_ptr(_Function_new_large<_Vt>(_STD forward<_CTypes>(_Args)...)); } else { - ::new (_Data._Buf_ptr<_Vt>()) _Vt(_STD forward<_Types>(_Args)...); + ::new (_Data._Buf_ptr<_Vt>()) _Vt(_STD forward<_CTypes>(_Args)...); } } @@ -1406,11 +1406,12 @@ public: return static_cast(_Data._Impl); } - template + template _NODISCARD static constexpr _Impl_t _Create_impl() noexcept { + _Impl_t _Impl{}; if constexpr (_Large_function_engaged<_Vt>()) { - _Impl._Invoke = _Function_invoke_large<_Vt, _Rx, _Types...>; + _Impl._Invoke = _Function_invoke_large<_Vt, _VtInvQuals, _Rx, _Types...>; _Impl._Move = _Function_move_large; if constexpr (is_trivially_destructible_v<_Vt>) { @@ -1423,7 +1424,7 @@ public: _Impl._Destroy = _Function_destroy_large<_Vt>; } } else { - _Impl._Invoke = _Function_invoke_small<_Vt, _Rx, _Types...>; + _Impl._Invoke = _Function_invoke_small<_Vt, _VtInvQuals, _Rx, _Types...>; if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) { _Impl._Move = @@ -1441,9 +1442,9 @@ public: return _Impl; } - template + template _NODISCARD static const _Impl_t* _Create_impl_ptr() noexcept { - static constexpr _Impl_t _Impl = _Create_impl<_Vt>(); + static constexpr _Impl_t _Impl = _Create_impl<_Vt, _VtInvQuals>(); return &_Impl; } }; @@ -1479,7 +1480,23 @@ public: template class move_only_function : private _Move_only_function_call<_Fx> { private: - using _Call = _Move_only_function_call<_Fx>; + using _Call = _Move_only_function_call<_Fx>; + + template + static constexpr bool _Enable_one_arg_constructor() { + return !is_same_v<_Remove_cvref_t<_Fn>, move_only_function> && // + !_Is_specialization_v<_Remove_cvref_t<_Fn>, in_place_type_t>; + } + + template + static constexpr bool _Enable_in_place_constructor() { + return is_constructible_v, _CTypes...>; + } + + template + static constexpr bool _Enable_in_place_list_constructor() { + return is_constructible_v, initializer_list<_Ux>, _CTypes...>; + } public: using typename _Call::result_type; @@ -1492,7 +1509,7 @@ public: } move_only_function(move_only_function&&) noexcept = default; - template + template (), int> = 0> move_only_function(_Fn&& _Callable) { using _Vt = decay_t<_Fn>; static_assert(is_constructible_v<_Vt, _Fn>); @@ -1504,7 +1521,23 @@ public: } } - this->_Construct_with_fn<_Vt>(_STD forward<_Fn>(_Callable)); + this->template _Construct_with_fn<_Vt, _Vt&>(_STD forward<_Fn>(_Callable)); + } + + template (), int> = 0> + move_only_function(in_place_type_t<_Fn>, _CTypes&&... _Args) { + using _Vt = decay_t<_Fn>; + static_assert(is_same_v<_Vt,_Fn>); + + this->template _Construct_with_fn<_Vt, _Vt&>(_STD forward<_CTypes>(_Args)...); + } + + template (), int> = 0> + 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>); + + this->template _Construct_with_fn<_Vt, _Vt&>(_Li, _STD forward<_CTypes>(_Args)...); } _NODISCARD explicit operator bool() const noexcept { diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 1316c263826..8e6fd2fba82 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -117,6 +117,15 @@ void test_impl(Args... args) { assert(counter::copies == 1); counter::copies = 0; } + + if constexpr (is_class_v) { + counter::copies = 0; + counter::moves = 0; + } + test_function_t constructed_in_place(in_place_type, args...); + assert(constructed_in_place(23, x) == 38); + assert(counter::copies == 0); + assert(counter::moves == 0); } if constexpr (is_class_v) { @@ -141,4 +150,5 @@ int main() { test_impl(); test_impl(); test_impl(plain_callable); + test_empty(); } From 037315864db464dd88387e2227c8c08d246f049e Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 12:49:45 +0300 Subject: [PATCH 035/112] clang format --- stl/inc/functional | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index fd47064b47c..d3ef381e945 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1480,7 +1480,7 @@ public: template class move_only_function : private _Move_only_function_call<_Fx> { private: - using _Call = _Move_only_function_call<_Fx>; + using _Call = _Move_only_function_call<_Fx>; template static constexpr bool _Enable_one_arg_constructor() { @@ -1527,15 +1527,16 @@ public: template (), int> = 0> move_only_function(in_place_type_t<_Fn>, _CTypes&&... _Args) { using _Vt = decay_t<_Fn>; - static_assert(is_same_v<_Vt,_Fn>); + static_assert(is_same_v<_Vt, _Fn>); this->template _Construct_with_fn<_Vt, _Vt&>(_STD forward<_CTypes>(_Args)...); } - template (), int> = 0> + template (), int> = 0> 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>); + static_assert(is_same_v<_Vt, _Fn>); this->template _Construct_with_fn<_Vt, _Vt&>(_Li, _STD forward<_CTypes>(_Args)...); } From f7e4828d3b57c3af6ea826dc4793dc1e87746fb0 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 12:54:59 +0300 Subject: [PATCH 036/112] clang format --- tests/std/tests/P0288R9_move_only_function/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 8e6fd2fba82..f705b9c2c79 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -120,7 +120,7 @@ void test_impl(Args... args) { if constexpr (is_class_v) { counter::copies = 0; - counter::moves = 0; + counter::moves = 0; } test_function_t constructed_in_place(in_place_type, args...); assert(constructed_in_place(23, x) == 38); From bbee00f9542565970ccfe7ed706c7c0d7a74c17d Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 14:18:46 +0300 Subject: [PATCH 037/112] missing operations --- stl/inc/functional | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index d3ef381e945..6c5343c5d26 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1408,7 +1408,6 @@ public: template _NODISCARD static constexpr _Impl_t _Create_impl() noexcept { - _Impl_t _Impl{}; if constexpr (_Large_function_engaged<_Vt>()) { _Impl._Invoke = _Function_invoke_large<_Vt, _VtInvQuals, _Rx, _Types...>; @@ -1525,7 +1524,7 @@ public: } template (), int> = 0> - move_only_function(in_place_type_t<_Fn>, _CTypes&&... _Args) { + explicit move_only_function(in_place_type_t<_Fn>, _CTypes&&... _Args) { using _Vt = decay_t<_Fn>; static_assert(is_same_v<_Vt, _Fn>); @@ -1534,18 +1533,42 @@ public: template (), int> = 0> - move_only_function(in_place_type_t<_Fn>, initializer_list<_Ux> _Li, _CTypes&&... _Args) { + 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>); this->template _Construct_with_fn<_Vt, _Vt&>(_Li, _STD forward<_CTypes>(_Args)...); } + move_only_function& operator=(move_only_function&& _Other) { + move_only_function(_STD move(_Other)).swap(*this); + return *this; + } + + move_only_function& operator=(nullptr_t) noexcept { + this->_Get_impl()->_Destroy(); + this->_Construct_with_null(); + } + + template + move_only_function& operator=(_Fn&& _Callable) { + move_only_function(_STD forward(_Callable)).swap(*this); + return *this; + } + _NODISCARD explicit operator bool() const noexcept { return !this->_Is_null(); } using _Call::operator(); + + void swap(move_only_function&) noexcept { + _CSTD abort(); + } + + 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(); From 40182b165e4936888f8e404cad9e36eb7d9ef042 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 14:29:43 +0300 Subject: [PATCH 038/112] clang format --- stl/inc/functional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 6c5343c5d26..cd427c9d196 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1544,7 +1544,7 @@ public: move_only_function(_STD move(_Other)).swap(*this); return *this; } - + move_only_function& operator=(nullptr_t) noexcept { this->_Get_impl()->_Destroy(); this->_Construct_with_null(); @@ -1561,7 +1561,7 @@ public: } using _Call::operator(); - + void swap(move_only_function&) noexcept { _CSTD abort(); } From ac8b0800a9d845ba2f51f5a86eb0f89384b6b3bd Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 17:39:50 +0300 Subject: [PATCH 039/112] assignment and swap --- stl/inc/functional | 60 +++++--- .../tests/P0288R9_move_only_function/test.cpp | 137 ++++++++++++++++-- 2 files changed, 169 insertions(+), 28 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index cd427c9d196..3b1e4d6261f 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1009,7 +1009,7 @@ private: template 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) \ @@ -1331,6 +1331,15 @@ void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_ _CSTD memcpy(&_Self._Data, &_Src._Data, _Size); // Copy Impl* and functor data } +template +constexpr size_t _Function_small_copy_size() noexcept { + // We copy Impl* and and functor data at once. + // Also is sometimes more efficient to memcpy whole pointers than byte remainings. + return _Move_only_function_data::_Buf_offset<_Vt>() + // Impl* and possible padding + size_t{sizeof(_Vt) + sizeof(void*) - 1} + & ~size_t{sizeof(void*) - 1}; // size rounded up to pointer +} + template _NODISCARD void* _Function_new_large(_Types&&... _Args) { struct _NODISCARD _Guard_type { @@ -1388,6 +1397,25 @@ public: _Get_impl()->_Destroy(_Data); } + void _Move_assign(_Move_only_function_base&& _Other) { + // GH-2278 - not implementing using swap as Standard asks for + + _Get_impl()->_Destroy(_Data); + _Other._Get_impl()->_Move(_Data, _Other._Data); + _Other._Data._Impl = &_Null_move_only_function<_Rx, _Types...>; + } + + void _Swap(_Move_only_function_base& _Other) { + const auto* _This_impl = _Get_impl(); + const auto* _That_impl = _Other._Get_impl(); + + _Move_only_function_data _Tmp; + + _This_impl->_Move(_Tmp, _Data); + _That_impl->_Move(_Data, _Other._Data); + _This_impl->_Move(_Other._Data, _Tmp); + } + _NODISCARD bool _Is_null() const noexcept { return _Data._Impl == &_Null_move_only_function<_Rx, _Types...>; } @@ -1398,9 +1426,6 @@ public: || !is_nothrow_move_constructible_v<_Vt> || alignof(_Vt) > alignof(max_align_t); } - static constexpr size_t _Round_up_size(size_t _Size) noexcept { - return size_t{_Size + sizeof(void*) - 1} & ~size_t{sizeof(void*) - 1}; - } _NODISCARD const _Impl_t* _Get_impl() const noexcept { return static_cast(_Data._Impl); @@ -1426,8 +1451,7 @@ public: _Impl._Invoke = _Function_invoke_small<_Vt, _VtInvQuals, _Rx, _Types...>; if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) { - _Impl._Move = - _Function_move_memcpy<_Move_only_function_data::_Buf_offset<_Vt>() + _Round_up_size(sizeof(_Vt))>; + _Impl._Move = _Function_move_memcpy<_Function_small_copy_size<_Vt>()>; } else { _Impl._Move = _Function_move_small<_Vt>; } @@ -1450,8 +1474,8 @@ public: template class _Move_only_function_call { - static_assert(_Always_false<_Fn>, "std::move_only_function only accepts functions as template argument (with " - "possibly const/ref/noexcept qualifiers)."); + static_assert(_Always_false<_Fn>, "std::move_only_function only accepts function types as template arguments, " + "with possibly const/ref/noexcept qualifiers."); }; template @@ -1540,19 +1564,21 @@ public: this->template _Construct_with_fn<_Vt, _Vt&>(_Li, _STD forward<_CTypes>(_Args)...); } - move_only_function& operator=(move_only_function&& _Other) { - move_only_function(_STD move(_Other)).swap(*this); - return *this; - } - move_only_function& operator=(nullptr_t) noexcept { this->_Get_impl()->_Destroy(); this->_Construct_with_null(); } + move_only_function& operator=(move_only_function&& _Other) { + // GH-2278 - not implementing using swap as Standard asks for + this->_Move_assign(_STD move(_Other)); + return *this; + } + template move_only_function& operator=(_Fn&& _Callable) { - move_only_function(_STD forward(_Callable)).swap(*this); + // GH-2278 - not implementing using swap as Standard asks for + this->_Move_assign(move_only_function(_STD forward<_Fn>(_Callable))); return *this; } @@ -1562,12 +1588,12 @@ public: using _Call::operator(); - void swap(move_only_function&) noexcept { - _CSTD abort(); + 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); + _Fn1._Swap(_Fn2); } _NODISCARD friend bool operator==(const move_only_function& _This, nullptr_t) noexcept { diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index f705b9c2c79..2f2ef59c5a5 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -63,15 +63,30 @@ struct small_callable : counter { small_callable(small_callable&&) noexcept = default; }; -struct large_callable : small_callable { +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 38; + return 40; } odd_cc_callable() = default; @@ -80,29 +95,48 @@ struct odd_cc_callable : counter { 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 38; + return 42; } using test_function_t = move_only_function; template -void test_impl(Args... 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) == 38); + assert(constructed_directly(23, x) == expect); assert(constructed_directly); assert(constructed_directly != nullptr); test_function_t move_constructed = std::move(constructed_directly); - assert(move_constructed(23, x) == 38); + assert(move_constructed(23, x) == expect); assert(!constructed_directly); assert(constructed_directly == nullptr); @@ -123,7 +157,7 @@ void test_impl(Args... args) { counter::moves = 0; } test_function_t constructed_in_place(in_place_type, args...); - assert(constructed_in_place(23, x) == 38); + assert(constructed_in_place(23, x) == expect); assert(counter::copies == 0); assert(counter::moves == 0); } @@ -133,10 +167,88 @@ void test_impl(Args... args) { } } +void test_assign() { + pass_this_by_ref x{63}; + + { + test_function_t f1{small_callable{}}; + test_function_t f2{large_callable{}}; + f2 = std::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 = std::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 = std::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 = std::move(f1); + assert(f2(23, x) == 39); + f1 = large_implicit_ptr_callable{}; + assert(f1(23, x) == 41); + } +} + +void test_swap() { + pass_this_by_ref x{63}; + + { + test_function_t f1{small_callable{}}; + test_function_t f2{large_callable{}}; + std::swap(f1, f2); + assert(f2(23, x) == 38); + assert(f1(23, x) == 39); + } + + { + test_function_t f1{large_callable{}}; + test_function_t f2{small_callable{}}; + f2.swap(f1); + assert(f2(23, x) == 39); + assert(f1(23, x) == 38); + } + + { + test_function_t f1{small_callable{}}; + test_function_t f2{odd_cc_callable{}}; + swap(f1, 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{}}; + swap(f1, f2); + assert(f2(23, x) == 39); + assert(f1(23, x) == 41); + } +} + + void test_empty() { test_function_t emtpty; assert(!emtpty); assert(emtpty == nullptr); + assert(nullptr == emtpty); test_function_t emtpty_moved = std::move(emtpty); assert(!emtpty_moved); @@ -146,9 +258,12 @@ void test_empty() { } int main() { - test_impl(); - test_impl(); - test_impl(); - test_impl(plain_callable); + test_construct_impl(38); + test_construct_impl(39); + test_construct_impl(40); + test_construct_impl(41); + test_construct_impl(42, plain_callable); + test_assign(); + test_swap(); test_empty(); } From c7e3b4c0437b416d1c040d17d31cbdbc9f949204 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 17:46:49 +0300 Subject: [PATCH 040/112] self-assignment --- stl/inc/functional | 4 +++- tests/std/tests/P0288R9_move_only_function/test.cpp | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 3b1e4d6261f..e2f23603179 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1571,7 +1571,9 @@ public: move_only_function& operator=(move_only_function&& _Other) { // GH-2278 - not implementing using swap as Standard asks for - this->_Move_assign(_STD move(_Other)); + if (this != &_Other) { + this->_Move_assign(_STD move(_Other)); + } return *this; } diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 2f2ef59c5a5..d23c0e38137 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -177,6 +177,8 @@ void test_assign() { assert(f2(23, x) == 38); f1 = large_callable{}; assert(f1(23, x) == 39); + f1 = std::move(f1); + assert(f1(23, x) == 39); } { @@ -186,6 +188,8 @@ void test_assign() { assert(f2(23, x) == 39); f1 = small_callable{}; assert(f1(23, x) == 38); + f1 = std::move(f1); + assert(f1(23, x) == 38); } { From bc0a9313160562027562f7a8f31c7fceb7eb838c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 18:03:03 +0300 Subject: [PATCH 041/112] clang warning --- tests/std/tests/P0288R9_move_only_function/test.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index d23c0e38137..cf80a52d68a 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -167,6 +167,12 @@ void test_construct_impl(int expect, Args... args) { } } +#ifdef __clang__ +// deliberate self-move as a test case +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wself-move" +#endif + void test_assign() { pass_this_by_ref x{63}; @@ -211,6 +217,10 @@ void test_assign() { } } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + void test_swap() { pass_this_by_ref x{63}; From 54924b890c6738b56da388eabeff35e5c6b66bea Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 19:00:19 +0300 Subject: [PATCH 042/112] spawn specializations --- stl/inc/functional | 215 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 197 insertions(+), 18 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index e2f23603179..eda8edc283e 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1483,21 +1483,197 @@ class _Move_only_function_call<_Rx(_Types...)> : public _Move_only_function_base public: using result_type = _Rx; + template + using _VtInvQuals = _Vt&; + + template + 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_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); } }; +template +class _Move_only_function_call<_Rx(_Types...)&> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + template + using _VtInvQuals = _Vt&; + + template + static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&, _Types...>; + + _Rx operator()(_Types&&... _Args) & { + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; + +template +class _Move_only_function_call<_Rx(_Types...) &&> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + template + using _VtInvQuals = _Vt&&; + + template + static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&&, _Types...>; + + _Rx operator()(_Types&&... _Args) && { + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; + +template +class _Move_only_function_call<_Rx(_Types...) const> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + template + using _VtInvQuals = _Vt const&; + + template + static constexpr bool _Is_callable_from = + is_invocable_r_v<_Rx, _Vt const, _Types...>&& is_invocable_r_v<_Rx, _Vt const&, _Types...>; + + _Rx operator()(_Types&&... _Args) const { + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; + +template +class _Move_only_function_call<_Rx(_Types...) const&> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + template + using _VtInvQuals = _Vt const&; + + template + static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt const&, _Types...>; + + _Rx operator()(_Types&&... _Args) const& { + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; + +template +class _Move_only_function_call<_Rx(_Types...) const&&> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + template + using _VtInvQuals = _Vt const&&; + + template + static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt const&&, _Types...>; + + _Rx operator()(_Types&&... _Args) const&& { + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; + #ifdef __cpp_noexcept_function_type template class _Move_only_function_call<_Rx(_Types...) noexcept> : public _Move_only_function_base<_Rx, _Types...> { public: using result_type = _Rx; + template + using _VtInvQuals = _Vt&; + + template + 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_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); } }; + +template +class _Move_only_function_call<_Rx(_Types...)& noexcept> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + template + using _VtInvQuals = _Vt&; + + template + static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>; + + _Rx operator()(_Types&&... _Args) & noexcept { + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; + +template +class _Move_only_function_call<_Rx(_Types...)&& noexcept> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + template + using _VtInvQuals = _Vt&&; + + template + static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&&, _Types...>; + + _Rx operator()(_Types&&... _Args) && noexcept { + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; + +template +class _Move_only_function_call<_Rx(_Types...) const noexcept> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + template + using _VtInvQuals = _Vt const&; + + template + static constexpr bool _Is_callable_from = + is_nothrow_invocable_r_v<_Rx, _Vt const, _Types...>&& is_nothrow_invocable_r_v<_Rx, _Vt const&, _Types...>; + + _Rx operator()(_Types&&... _Args) const noexcept { + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; + +template +class _Move_only_function_call<_Rx(_Types...) const& noexcept> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + template + using _VtInvQuals = _Vt const&; + + template + static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt const&, _Types...>; + + _Rx operator()(_Types&&... _Args) const& noexcept { + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; + +template +class _Move_only_function_call<_Rx(_Types...) const&& noexcept> : public _Move_only_function_base<_Rx, _Types...> { +public: + using result_type = _Rx; + + template + using _VtInvQuals = _Vt const&&; + + template + static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt const&&, _Types...>; + + _Rx operator()(_Types&&... _Args) const&& noexcept { + return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + } +}; #endif template @@ -1505,22 +1681,23 @@ class move_only_function : private _Move_only_function_call<_Fx> { private: using _Call = _Move_only_function_call<_Fx>; + // clang-format off template - static constexpr bool _Enable_one_arg_constructor() { - return !is_same_v<_Remove_cvref_t<_Fn>, move_only_function> && // - !_Is_specialization_v<_Remove_cvref_t<_Fn>, in_place_type_t>; - } + 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>; template - static constexpr bool _Enable_in_place_constructor() { - return is_constructible_v, _CTypes...>; - } + static constexpr bool _Enable_in_place_constructor = + is_constructible_v, _CTypes...> && + _Call::template _Is_callable_from>; template - static constexpr bool _Enable_in_place_list_constructor() { - return is_constructible_v, initializer_list<_Ux>, _CTypes...>; - } - + static constexpr bool _Enable_in_place_list_constructor = + is_constructible_v, initializer_list<_Ux>, _CTypes...> && + _Call::template _Is_callable_from>; + // clang-format on public: using typename _Call::result_type; @@ -1532,7 +1709,7 @@ public: } move_only_function(move_only_function&&) noexcept = default; - template (), int> = 0> + template , int> = 0> move_only_function(_Fn&& _Callable) { using _Vt = decay_t<_Fn>; static_assert(is_constructible_v<_Vt, _Fn>); @@ -1544,24 +1721,26 @@ public: } } - this->template _Construct_with_fn<_Vt, _Vt&>(_STD forward<_Fn>(_Callable)); + using _VtInvQuals = typename _Call::template _VtInvQuals<_Vt>; + this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable)); } - template (), int> = 0> + template , 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>); - this->template _Construct_with_fn<_Vt, _Vt&>(_STD forward<_CTypes>(_Args)...); + using _VtInvQuals = typename _Call::template _VtInvQuals<_Vt>; + this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_CTypes>(_Args)...); } - template (), int> = 0> + template , 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>); - this->template _Construct_with_fn<_Vt, _Vt&>(_Li, _STD forward<_CTypes>(_Args)...); + using _VtInvQuals = typename _Call::template _VtInvQuals<_Vt>; + this->template _Construct_with_fn<_Vt, _VtInvQuals>(_Li, _STD forward<_CTypes>(_Args)...); } move_only_function& operator=(nullptr_t) noexcept { From 96a4ae968beb54e0db8e9a4f15aabd9b830bd3b9 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 19:13:57 +0300 Subject: [PATCH 043/112] clang format --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index eda8edc283e..46c1b84f277 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1694,7 +1694,7 @@ private: _Call::template _Is_callable_from>; template - static constexpr bool _Enable_in_place_list_constructor = + static constexpr bool _Enable_in_place_list_constructor = is_constructible_v, initializer_list<_Ux>, _CTypes...> && _Call::template _Is_callable_from>; // clang-format on From 3a47d12bee1211b8d140d1a06677a948b66f961c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 20:12:19 +0300 Subject: [PATCH 044/112] Perf: making special cases --- stl/inc/functional | 80 +++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 46c1b84f277..95d948c7ec0 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1259,7 +1259,7 @@ struct _Move_only_function_impl { // per-callable-type structure acting as a vir _Rx (*_Invoke)(_Move_only_function_data&, _Types&&...); // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") void (*_Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; - // destroys data (not resetting its "vtable") + // Destroys data (not resetting its "vtable") void (*_Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR; }; @@ -1268,17 +1268,11 @@ template _CSTD abort(); // We are not std::function to throw bad_function_call } -inline void _Function_empty_move(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { - _Self._Impl = _Src._Impl; // Function data is empty, still need to copy "vtable" -} - -inline void _Function_empty_destroy(_Move_only_function_data&) noexcept {} - template inline constexpr _Move_only_function_impl<_Rx, _Types...> _Null_move_only_function = { _Function_no_callable<_Rx, _Types...>, - _Function_empty_move, - _Function_empty_destroy, + nullptr, + nullptr, }; template @@ -1333,11 +1327,12 @@ void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_ template constexpr size_t _Function_small_copy_size() noexcept { - // We copy Impl* and and functor data at once. - // Also is sometimes more efficient to memcpy whole pointers than byte remainings. - return _Move_only_function_data::_Buf_offset<_Vt>() + // Impl* and possible padding - size_t{sizeof(_Vt) + sizeof(void*) - 1} - & ~size_t{sizeof(void*) - 1}; // size rounded up to pointer + // We copy Impl* and and functor data at once + constexpr size_t _Impl_ptr_and_padding = _Move_only_function_data::_Buf_offset<_Vt>(); + // Sometimes more efficient to memcpy whole pointers than byte remainings + constexpr size_t _Round_up_ptr = size_t{sizeof(_Vt) + sizeof(void*) - 1} & ~size_t{sizeof(void*) - 1}; + + return _Impl_ptr_and_padding + _Round_up_ptr; } template @@ -1375,12 +1370,17 @@ public: _Move_only_function_base() noexcept = default; // leaves fields uninitialized _Move_only_function_base(_Move_only_function_base&& _Other) noexcept { - _Other._Get_impl()->_Move(_Data, _Other._Data); - _Other._Data._Impl = &_Null_move_only_function<_Rx, _Types...>; + this->_Move(_Data, _Other._Data); + _Other._Reset_to_null(); } void _Construct_with_null() noexcept { _Data._Impl = &_Null_move_only_function<_Rx, _Types...>; + _Data._Set_large_fn_ptr(nullptr); // initialize, since we'll be copying it + } + + void _Reset_to_null() noexcept { + _Data._Impl = &_Null_move_only_function<_Rx, _Types...>; } template @@ -1394,26 +1394,39 @@ public: } ~_Move_only_function_base() { - _Get_impl()->_Destroy(_Data); + _Destroy(_Data); + } + + static void _Destroy(_Move_only_function_data& _Data) { + const auto* _Impl = static_cast(_Data._Impl); + if (_Impl->_Destroy) { + _Impl->_Destroy(_Data); + } + } + + static void _Move(_Move_only_function_data& _Data, _Move_only_function_data& _Src) { + const auto* _Impl = static_cast(_Src._Impl); + if (!_Impl->_Move) { + _Function_move_large(_Data, _Src); + } else { + _Impl->_Move(_Data, _Src); + } } void _Move_assign(_Move_only_function_base&& _Other) { // GH-2278 - not implementing using swap as Standard asks for - _Get_impl()->_Destroy(_Data); - _Other._Get_impl()->_Move(_Data, _Other._Data); - _Other._Data._Impl = &_Null_move_only_function<_Rx, _Types...>; + _Destroy(_Data); + _Move(_Data, _Other._Data); + _Other._Reset_to_null(); } void _Swap(_Move_only_function_base& _Other) { - const auto* _This_impl = _Get_impl(); - const auto* _That_impl = _Other._Get_impl(); - _Move_only_function_data _Tmp; - _This_impl->_Move(_Tmp, _Data); - _That_impl->_Move(_Data, _Other._Data); - _This_impl->_Move(_Other._Data, _Tmp); + _Move(_Tmp, _Data); + _Move(_Data, _Other._Data); + _Move(_Other._Data, _Tmp); } _NODISCARD bool _Is_null() const noexcept { @@ -1426,7 +1439,6 @@ public: || !is_nothrow_move_constructible_v<_Vt> || alignof(_Vt) > alignof(max_align_t); } - _NODISCARD const _Impl_t* _Get_impl() const noexcept { return static_cast(_Data._Impl); } @@ -1436,7 +1448,7 @@ public: _Impl_t _Impl{}; if constexpr (_Large_function_engaged<_Vt>()) { _Impl._Invoke = _Function_invoke_large<_Vt, _VtInvQuals, _Rx, _Types...>; - _Impl._Move = _Function_move_large; + _Impl._Move = nullptr; if constexpr (is_trivially_destructible_v<_Vt>) { if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { @@ -1451,13 +1463,17 @@ public: _Impl._Invoke = _Function_invoke_small<_Vt, _VtInvQuals, _Rx, _Types...>; if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) { - _Impl._Move = _Function_move_memcpy<_Function_small_copy_size<_Vt>()>; + if constexpr (_Function_small_copy_size<_Vt>() > sizeof(void*) * 2) { + _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 = _Function_empty_destroy; + _Impl._Destroy = nullptr; } else { _Impl._Destroy = _Function_destroy_small<_Vt>; } @@ -1744,8 +1760,8 @@ public: } move_only_function& operator=(nullptr_t) noexcept { - this->_Get_impl()->_Destroy(); - this->_Construct_with_null(); + _Destroy(this->_Data); + this->Reset_to_null(); } move_only_function& operator=(move_only_function&& _Other) { From 47a2d26937bd8c717ce2bdd7c1aca492dedfd925 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 14 Oct 2021 20:15:13 +0300 Subject: [PATCH 045/112] optimize _Function_move_memcpy --- stl/inc/functional | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 95d948c7ec0..03aab624772 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1322,7 +1322,9 @@ void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { template void _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 + // Copy Impl* and functor data. + _Self._Pointers[0] = _Src._Pointers[0]; + _Self._Pointers[1] = _Src._Pointers[1]; } template From e37dc105086b7e9088d40b46dd4f0a5d3b5bf916 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 15 Oct 2021 14:15:03 +0300 Subject: [PATCH 046/112] simplify by hiding _Invoke obtaining --- stl/inc/functional | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 03aab624772..5f67dea894b 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1441,8 +1441,8 @@ public: || !is_nothrow_move_constructible_v<_Vt> || alignof(_Vt) > alignof(max_align_t); } - _NODISCARD const _Impl_t* _Get_impl() const noexcept { - return static_cast(_Data._Impl); + _NODISCARD auto* _Get_invoke() const noexcept { + return static_cast(_Data._Impl)->_Invoke; } template @@ -1509,7 +1509,7 @@ public: is_invocable_r_v<_Rx, _Vt, _Types...>&& is_invocable_r_v<_Rx, _Vt&, _Types...>; _Rx operator()(_Types&&... _Args) { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1525,7 +1525,7 @@ public: static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&, _Types...>; _Rx operator()(_Types&&... _Args) & { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1541,7 +1541,7 @@ public: static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&&, _Types...>; _Rx operator()(_Types&&... _Args) && { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1558,7 +1558,7 @@ public: is_invocable_r_v<_Rx, _Vt const, _Types...>&& is_invocable_r_v<_Rx, _Vt const&, _Types...>; _Rx operator()(_Types&&... _Args) const { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1574,7 +1574,7 @@ public: static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt const&, _Types...>; _Rx operator()(_Types&&... _Args) const& { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1590,7 +1590,7 @@ public: static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt const&&, _Types...>; _Rx operator()(_Types&&... _Args) const&& { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1608,7 +1608,7 @@ public: is_nothrow_invocable_r_v<_Rx, _Vt, _Types...>&& is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>; _Rx operator()(_Types&&... _Args) noexcept { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1624,7 +1624,7 @@ public: static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>; _Rx operator()(_Types&&... _Args) & noexcept { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1640,7 +1640,7 @@ public: static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&&, _Types...>; _Rx operator()(_Types&&... _Args) && noexcept { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1657,7 +1657,7 @@ public: is_nothrow_invocable_r_v<_Rx, _Vt const, _Types...>&& is_nothrow_invocable_r_v<_Rx, _Vt const&, _Types...>; _Rx operator()(_Types&&... _Args) const noexcept { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1673,7 +1673,7 @@ public: static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt const&, _Types...>; _Rx operator()(_Types&&... _Args) const& noexcept { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1689,7 +1689,7 @@ public: static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt const&&, _Types...>; _Rx operator()(_Types&&... _Args) const&& noexcept { - return this->_Get_impl()->_Invoke(this->_Data, _STD forward<_Types>(_Args)...); + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; #endif From c57f41f975496a68790836447496018142e4e4ec Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 15 Oct 2021 14:52:34 +0300 Subject: [PATCH 047/112] weird operator overload protection --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 5f67dea894b..8f1d05a6b03 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1768,7 +1768,7 @@ public: move_only_function& operator=(move_only_function&& _Other) { // GH-2278 - not implementing using swap as Standard asks for - if (this != &_Other) { + if (this != _STD addressof(_Other)) { this->_Move_assign(_STD move(_Other)); } return *this; From 0fc84704e9f6ba5f61c4feb9d8e4f4538c54a55a Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 15 Oct 2021 15:41:27 +0300 Subject: [PATCH 048/112] #endif comments, inline --- stl/inc/functional | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 8f1d05a6b03..a7abc356c6f 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1276,17 +1276,17 @@ inline constexpr _Move_only_function_impl<_Rx, _Types...> _Null_move_only_functi }; template -_NODISCARD _Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types&&... _Args) { +_NODISCARD inline _Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types&&... _Args) { return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } template -_NODISCARD _Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types&&... _Args) { +_NODISCARD inline _Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types&&... _Args) { return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } template -void _Function_move_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { +inline void _Function_move_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { ::new (_Self._Buf_ptr<_Vt>()) _Vt(_STD move(*_Src._Small_fn_ptr<_Vt>())); _Src._Small_fn_ptr<_Vt>()->~_Vt(); _Self._Impl = _Src._Impl; @@ -1297,7 +1297,7 @@ inline void _Function_move_large(_Move_only_function_data& _Self, _Move_only_fun } template -void _Function_destroy_small(_Move_only_function_data& _Self) noexcept { +inline void _Function_destroy_small(_Move_only_function_data& _Self) noexcept { _Self._Small_fn_ptr<_Vt>()->~_Vt(); } @@ -1306,22 +1306,23 @@ inline void _Function_deallocate_large_default_aligned(_Move_only_function_data& } template -void _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noexcept { +inline void _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noexcept { ::operator delete (_Self._Large_fn_ptr(), align_val_t{_Align}); } template -void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { - _Self._Large_fn_ptr<_Vt>()->~_Vt(); +inline void _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__) { - _Function_deallocate_large_default_aligned(_Self); + ::operator delete(_Pfn); } else { - _Function_deallocate_large_overaligned(_Self); + ::operator delete (_Pfn, align_val_t{alignof(_Vt)}); } } template -void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { +inline void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { // Copy Impl* and functor data. _Self._Pointers[0] = _Src._Pointers[0]; _Self._Pointers[1] = _Src._Pointers[1]; @@ -1692,7 +1693,7 @@ public: return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; -#endif +#endif // __cpp_noexcept_function_type template class move_only_function : private _Move_only_function_call<_Fx> { @@ -1799,7 +1800,7 @@ public: return _This._Is_null(); } }; -#endif +#endif // _HAS_CXX23 template struct _Ph { // placeholder From 0a151c52d904158b61659d820624dd032f4ffc08 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 15 Oct 2021 15:53:45 +0300 Subject: [PATCH 049/112] fix types --- stl/inc/functional | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index a7abc356c6f..14ef410543c 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1312,12 +1312,12 @@ inline void _Function_deallocate_large_overaligned(_Move_only_function_data& _Se template inline void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { - const auto* _Pfn = _Self._Large_fn_ptr<_Vt>(); + auto* _Pfn = _Self._Large_fn_ptr<_Vt>(); _Pfn->~_Vt(); if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { - ::operator delete(_Pfn); + ::operator delete(static_cast(_Pfn)); } else { - ::operator delete (_Pfn, align_val_t{alignof(_Vt)}); + ::operator delete (static_cast(_Pfn), align_val_t{alignof(_Vt)}); } } From 09ad49e614b35605dbd7db94dbd5ec9e4ea61a19 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 16:01:57 +0300 Subject: [PATCH 050/112] deoptimizing -- not enough evidence that the optimization is useful --- stl/inc/functional | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 14ef410543c..f729bacff83 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1268,11 +1268,17 @@ template _CSTD abort(); // We are not std::function to throw bad_function_call } +inline void _Function_empty_move(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { + _Self._Impl = _Src._Impl; // Function data is empty, still need to copy "vtable" +} + +inline void _Function_empty_destroy(_Move_only_function_data&) noexcept {} + template inline constexpr _Move_only_function_impl<_Rx, _Types...> _Null_move_only_function = { _Function_no_callable<_Rx, _Types...>, - nullptr, - nullptr, + _Function_empty_move, + _Function_empty_destroy, }; template @@ -1323,9 +1329,7 @@ inline void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { template inline void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { - // Copy Impl* and functor data. - _Self._Pointers[0] = _Src._Pointers[0]; - _Self._Pointers[1] = _Src._Pointers[1]; + _CSTD memcpy(&_Self._Data, &_Src._Data, _Size); // Copy Impl* and functor data } template @@ -1379,7 +1383,6 @@ public: void _Construct_with_null() noexcept { _Data._Impl = &_Null_move_only_function<_Rx, _Types...>; - _Data._Set_large_fn_ptr(nullptr); // initialize, since we'll be copying it } void _Reset_to_null() noexcept { @@ -1402,18 +1405,12 @@ public: static void _Destroy(_Move_only_function_data& _Data) { const auto* _Impl = static_cast(_Data._Impl); - if (_Impl->_Destroy) { - _Impl->_Destroy(_Data); - } + _Impl->_Destroy(_Data); } static void _Move(_Move_only_function_data& _Data, _Move_only_function_data& _Src) { const auto* _Impl = static_cast(_Src._Impl); - if (!_Impl->_Move) { - _Function_move_large(_Data, _Src); - } else { - _Impl->_Move(_Data, _Src); - } + _Function_move_large(_Data, _Src); } void _Move_assign(_Move_only_function_base&& _Other) { @@ -1451,7 +1448,7 @@ public: _Impl_t _Impl{}; if constexpr (_Large_function_engaged<_Vt>()) { _Impl._Invoke = _Function_invoke_large<_Vt, _VtInvQuals, _Rx, _Types...>; - _Impl._Move = nullptr; + _Impl._Move = _Function_move_large; if constexpr (is_trivially_destructible_v<_Vt>) { if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { @@ -1466,17 +1463,13 @@ public: _Impl._Invoke = _Function_invoke_small<_Vt, _VtInvQuals, _Rx, _Types...>; if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) { - if constexpr (_Function_small_copy_size<_Vt>() > sizeof(void*) * 2) { - _Impl._Move = _Function_move_memcpy<_Function_small_copy_size<_Vt>()>; - } else { - _Impl._Move = nullptr; - } + _Impl._Move = _Function_move_memcpy<_Function_small_copy_size<_Vt>()>; } else { _Impl._Move = _Function_move_small<_Vt>; } if constexpr (is_trivially_destructible_v<_Vt>) { - _Impl._Destroy = nullptr; + _Impl._Destroy = _Function_empty_destroy; } else { _Impl._Destroy = _Function_destroy_small<_Vt>; } From 0e47c0f03e84d6b4586a01f584cd6f2f4edd9ef2 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 16:27:22 +0300 Subject: [PATCH 051/112] fully restore virtual flow --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index f729bacff83..d9e859bb659 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1410,7 +1410,7 @@ public: static void _Move(_Move_only_function_data& _Data, _Move_only_function_data& _Src) { const auto* _Impl = static_cast(_Src._Impl); - _Function_move_large(_Data, _Src); + _Impl->Move(_Data, _Src); } void _Move_assign(_Move_only_function_base&& _Other) { From 7e8aa96ec2596929f43b1b80e3cba53986910b30 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 16:32:05 +0300 Subject: [PATCH 052/112] fully restore virtual flow --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index d9e859bb659..08ee81b0758 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1410,7 +1410,7 @@ public: static void _Move(_Move_only_function_data& _Data, _Move_only_function_data& _Src) { const auto* _Impl = static_cast(_Src._Impl); - _Impl->Move(_Data, _Src); + _Impl->_Move(_Data, _Src); } void _Move_assign(_Move_only_function_base&& _Other) { From 3f0fb7dae19e563e246ff56c1861bddac232f4cd Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 16:37:25 +0300 Subject: [PATCH 053/112] no, still optimiztion is useful --- stl/inc/functional | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 08ee81b0758..7a669877211 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1277,8 +1277,8 @@ inline void _Function_empty_destroy(_Move_only_function_data&) noexcept {} template inline constexpr _Move_only_function_impl<_Rx, _Types...> _Null_move_only_function = { _Function_no_callable<_Rx, _Types...>, - _Function_empty_move, - _Function_empty_destroy, + nullptr, + nullptr, }; template @@ -1299,7 +1299,9 @@ inline void _Function_move_small(_Move_only_function_data& _Self, _Move_only_fun } inline void _Function_move_large(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { - _CSTD memcpy(&_Self._Data, &_Src._Data, 2 * sizeof(void*)); // Copy Impl* and indirect data + // Copy Impl* and functor data. + _Self._Pointers[0] = _Src._Pointers[0]; + _Self._Pointers[1] = _Src._Pointers[1]; } template @@ -1383,6 +1385,7 @@ public: void _Construct_with_null() noexcept { _Data._Impl = &_Null_move_only_function<_Rx, _Types...>; + _Data._Set_large_fn_ptr(nullptr); // initialize, since we'll be copying it } void _Reset_to_null() noexcept { @@ -1405,12 +1408,18 @@ public: static void _Destroy(_Move_only_function_data& _Data) { const auto* _Impl = static_cast(_Data._Impl); - _Impl->_Destroy(_Data); + if (_Impl->_Destroy) { + _Impl->_Destroy(_Data); + } } static void _Move(_Move_only_function_data& _Data, _Move_only_function_data& _Src) { const auto* _Impl = static_cast(_Src._Impl); - _Impl->_Move(_Data, _Src); + if (!_Impl->_Move) { + _Function_move_large(_Data, _Src); + } else { + _Impl->_Move(_Data, _Src); + } } void _Move_assign(_Move_only_function_base&& _Other) { @@ -1448,7 +1457,7 @@ public: _Impl_t _Impl{}; if constexpr (_Large_function_engaged<_Vt>()) { _Impl._Invoke = _Function_invoke_large<_Vt, _VtInvQuals, _Rx, _Types...>; - _Impl._Move = _Function_move_large; + _Impl._Move = nullptr; if constexpr (is_trivially_destructible_v<_Vt>) { if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { @@ -1463,13 +1472,17 @@ public: _Impl._Invoke = _Function_invoke_small<_Vt, _VtInvQuals, _Rx, _Types...>; if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) { - _Impl._Move = _Function_move_memcpy<_Function_small_copy_size<_Vt>()>; + if constexpr (_Function_small_copy_size<_Vt>() > sizeof(void*) * 2) { + _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 = _Function_empty_destroy; + _Impl._Destroy = nullptr; } else { _Impl._Destroy = _Function_destroy_small<_Vt>; } From 26db3d6020934fd2e0e455c8ce436960d29f1363 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 16:38:03 +0300 Subject: [PATCH 054/112] empty not used --- stl/inc/functional | 6 ------ 1 file changed, 6 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 7a669877211..f7421e6dd21 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1268,12 +1268,6 @@ template _CSTD abort(); // We are not std::function to throw bad_function_call } -inline void _Function_empty_move(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { - _Self._Impl = _Src._Impl; // Function data is empty, still need to copy "vtable" -} - -inline void _Function_empty_destroy(_Move_only_function_data&) noexcept {} - template inline constexpr _Move_only_function_impl<_Rx, _Types...> _Null_move_only_function = { _Function_no_callable<_Rx, _Types...>, From b6dbc35d0c54a3c8f1367433f2853302de60bd6b Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 17:05:13 +0300 Subject: [PATCH 055/112] inline variables --- stl/inc/functional | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index f7421e6dd21..4f4b28c303b 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1216,22 +1216,16 @@ union alignas(max_align_t) _Move_only_function_data { char _Data; // For aliasing template - _NODISCARD static constexpr size_t _Buf_offset() noexcept { - if constexpr (alignof(_Fn) <= sizeof(_Impl)) { - return sizeof(_Impl); // Data immediately after impl - } else { - return alignof(_Fn); // Pad to next alignment - } - } + static constexpr size_t _Buf_offset = ((alignof(_Fn) <= sizeof(_Impl)) + ? sizeof(_Impl) // Data immediately after impl + : alignof(_Fn)); // Pad to next alignment template - _NODISCARD static constexpr size_t _Buf_size() noexcept { - return sizeof(_Pointers) - _Buf_offset<_Fn>(); - } + static constexpr size_t _Buf_size = sizeof(_Pointers) - _Buf_offset<_Fn>; template _NODISCARD constexpr void* _Buf_ptr() noexcept { - return &_Data + _Buf_offset<_Fn>(); + return &_Data + _Buf_offset<_Fn>; } template @@ -1329,14 +1323,9 @@ inline void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_fu } template -constexpr size_t _Function_small_copy_size() noexcept { - // We copy Impl* and and functor data at once - constexpr size_t _Impl_ptr_and_padding = _Move_only_function_data::_Buf_offset<_Vt>(); - // Sometimes more efficient to memcpy whole pointers than byte remainings - constexpr size_t _Round_up_ptr = size_t{sizeof(_Vt) + sizeof(void*) - 1} & ~size_t{sizeof(void*) - 1}; - - return _Impl_ptr_and_padding + _Round_up_ptr; -} +inline constexpr size_t _Function_small_copy_size = // We copy Impl* and and 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 _NODISCARD void* _Function_new_large(_Types&&... _Args) { @@ -1389,7 +1378,7 @@ public: template void _Construct_with_fn(_CTypes&&... _Args) { _Data._Impl = _Create_impl_ptr<_Vt, _VtInvQuals>(); - if constexpr (_Large_function_engaged<_Vt>()) { + 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)...); @@ -1437,10 +1426,9 @@ public: } template - _NODISCARD static constexpr bool _Large_function_engaged() noexcept { - return sizeof(_Vt) > _Move_only_function_data::_Buf_size<_Vt>() - || !is_nothrow_move_constructible_v<_Vt> || alignof(_Vt) > alignof(max_align_t); - } + 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(_Data._Impl)->_Invoke; @@ -1449,7 +1437,7 @@ public: template _NODISCARD static constexpr _Impl_t _Create_impl() noexcept { _Impl_t _Impl{}; - if constexpr (_Large_function_engaged<_Vt>()) { + if constexpr (_Large_function_engaged<_Vt>) { _Impl._Invoke = _Function_invoke_large<_Vt, _VtInvQuals, _Rx, _Types...>; _Impl._Move = nullptr; @@ -1466,8 +1454,8 @@ public: _Impl._Invoke = _Function_invoke_small<_Vt, _VtInvQuals, _Rx, _Types...>; if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) { - if constexpr (_Function_small_copy_size<_Vt>() > sizeof(void*) * 2) { - _Impl._Move = _Function_move_memcpy<_Function_small_copy_size<_Vt>()>; + if constexpr ((_Function_small_copy_size<_V>) > sizeof(void*) * 2) { + _Impl._Move = _Function_move_memcpy<_Function_small_copy_size<_Vt>>; } else { _Impl._Move = nullptr; } From bc8af9aeb4075934f49905648f51557619558b8f Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 17:07:35 +0300 Subject: [PATCH 056/112] restore memcpy in store-to-load-failure scenarios it will likely inline --- stl/inc/functional | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 4f4b28c303b..ff080c3a032 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1288,8 +1288,7 @@ inline void _Function_move_small(_Move_only_function_data& _Self, _Move_only_fun inline void _Function_move_large(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { // Copy Impl* and functor data. - _Self._Pointers[0] = _Src._Pointers[0]; - _Self._Pointers[1] = _Src._Pointers[1]; + _CSTD memcpy(&_Self._Data, &_Src._Data, 2*sizeof(void*)); // Copy Impl* and functor data } template From d7dc20659470e309de2028a0a0ec7e68ee340060 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 17:08:26 +0300 Subject: [PATCH 057/112] dup comment --- stl/inc/functional | 1 - 1 file changed, 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index ff080c3a032..dbc91f98b3f 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1287,7 +1287,6 @@ inline void _Function_move_small(_Move_only_function_data& _Self, _Move_only_fun } inline void _Function_move_large(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { - // Copy Impl* and functor data. _CSTD memcpy(&_Self._Data, &_Src._Data, 2*sizeof(void*)); // Copy Impl* and functor data } From 527dc15047dab9b9d123ae2c0289300a0bd712aa Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 17:16:08 +0300 Subject: [PATCH 058/112] clang format --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index dbc91f98b3f..98d170d4f7e 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1287,7 +1287,7 @@ inline void _Function_move_small(_Move_only_function_data& _Self, _Move_only_fun } inline void _Function_move_large(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { - _CSTD memcpy(&_Self._Data, &_Src._Data, 2*sizeof(void*)); // Copy Impl* and functor data + _CSTD memcpy(&_Self._Data, &_Src._Data, 2 * sizeof(void*)); // Copy Impl* and functor data } template From 81a4624cfa956d0c4ea93e9f01111cacf874ae79 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 17:43:16 +0300 Subject: [PATCH 059/112] _Vt --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 98d170d4f7e..8a0d509e3c3 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1452,7 +1452,7 @@ public: _Impl._Invoke = _Function_invoke_small<_Vt, _VtInvQuals, _Rx, _Types...>; if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) { - if constexpr ((_Function_small_copy_size<_V>) > sizeof(void*) * 2) { + if constexpr ((_Function_small_copy_size<_Vt>) > sizeof(void*) * 2) { _Impl._Move = _Function_move_memcpy<_Function_small_copy_size<_Vt>>; } else { _Impl._Move = nullptr; From 754876ad91390e193fdb4eaad3b618ba0ecaeb8d Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 18:55:49 +0300 Subject: [PATCH 060/112] embed _Impl_t --- stl/inc/functional | 51 ++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 8a0d509e3c3..40855675501 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1243,32 +1243,11 @@ union alignas(max_align_t) _Move_only_function_data { } }; -template -struct _Move_only_function_impl { // per-callable-type structure acting as a virtual function table - // using vtable emulations gives more flexibility for optimizations and reduces the need for RTTIs - // (RTTI saving may be significant as with lambdas and binds there may be a lot of distinct callable types, - // we don't have distinct wrapper class type for each callable type, only distinct functions when needed) - - // Calls target - _Rx (*_Invoke)(_Move_only_function_data&, _Types&&...); - // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") - void (*_Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; - // Destroys data (not resetting its "vtable") - void (*_Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR; -}; - template [[noreturn]] _Rx _Function_no_callable(_Move_only_function_data&, _Types&&...) noexcept { _CSTD abort(); // We are not std::function to throw bad_function_call } -template -inline constexpr _Move_only_function_impl<_Rx, _Types...> _Null_move_only_function = { - _Function_no_callable<_Rx, _Types...>, - nullptr, - nullptr, -}; - template _NODISCARD inline _Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types&&... _Args) { return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); @@ -1325,8 +1304,8 @@ inline constexpr size_t _Function_small_copy_size = // We copy Impl* and and fun _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 -_NODISCARD void* _Function_new_large(_Types&&... _Args) { +template +inline _NODISCARD void* _Function_new_large(_CTypes&&... _Args) { struct _NODISCARD _Guard_type { void* _Ptr; @@ -1354,7 +1333,25 @@ _NODISCARD void* _Function_new_large(_Types&&... _Args) { template class _Move_only_function_base { public: - using _Impl_t = _Move_only_function_impl<_Rx, _Types...>; + struct _Impl_t { // per-callable-type structure acting as a virtual function table + // using vtable emulations gives more flexibility for optimizations and reduces the need for RTTIs + // (RTTI saving may be significant as with lambdas and binds there may be a lot of distinct callable types, + // we don't have distinct wrapper class type for each callable type, only distinct functions when needed) + + // Calls target + _Rx (*_Invoke)(_Move_only_function_data&, _Types&&...); + // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") + void (*_Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; + // Destroys data (not resetting its "vtable") + void (*_Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR; + }; + + static constexpr _Impl_t _Null_move_only_function = { + _Function_no_callable<_Rx, _Types...>, + nullptr, + nullptr, + }; + _Move_only_function_data _Data; _Move_only_function_base() noexcept = default; // leaves fields uninitialized @@ -1365,12 +1362,12 @@ public: } void _Construct_with_null() noexcept { - _Data._Impl = &_Null_move_only_function<_Rx, _Types...>; + _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<_Rx, _Types...>; + _Data._Impl = &_Null_move_only_function; } template @@ -1420,7 +1417,7 @@ public: } _NODISCARD bool _Is_null() const noexcept { - return _Data._Impl == &_Null_move_only_function<_Rx, _Types...>; + return _Data._Impl == &_Null_move_only_function; } template From ccb15c96c2a32563331d463a65889529e1743cde Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 18:56:47 +0300 Subject: [PATCH 061/112] ctypes --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 40855675501..aec7be5639f 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1326,7 +1326,7 @@ inline _NODISCARD void* _Function_new_large(_CTypes&&... _Args) { } else { _Guard._Ptr = ::operator new (sizeof(_Vt), align_val_t{alignof(_Vt)}); } - ::new (_Guard._Ptr) _Vt(_STD forward<_Types>(_Args)...); + ::new (_Guard._Ptr) _Vt(_STD forward<_CTypes>(_Args)...); return _STD exchange(_Guard._Ptr, nullptr); } From 45160fbccafb009f6da94ca9a111077a5eb77c01 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 19:17:28 +0300 Subject: [PATCH 062/112] _NODISCARD inline --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index aec7be5639f..dcd21ea99a1 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1305,7 +1305,7 @@ inline constexpr size_t _Function_small_copy_size = // We copy Impl* and and fun (size_t{sizeof(_Vt) + sizeof(void*) - 1} & ~size_t{sizeof(void*) - 1}); // size in whole pointers template -inline _NODISCARD void* _Function_new_large(_CTypes&&... _Args) { +_NODISCARD inline void* _Function_new_large(_CTypes&&... _Args) { struct _NODISCARD _Guard_type { void* _Ptr; From b72dd6b6fcfd9e43952580b3481391a42dc94596 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 20:52:29 +0300 Subject: [PATCH 063/112] Play fair when moving --- stl/inc/functional | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index dcd21ea99a1..29d9b23f2cc 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1401,10 +1401,29 @@ public: } void _Move_assign(_Move_only_function_base&& _Other) { - // GH-2278 - not implementing using swap as Standard asks for - - _Destroy(_Data); - _Move(_Data, _Other._Data); + // As specified in P0288R9, we excepted to first move the new target, then finally destroy the old target. + // It is more efficient to do the reverse - this way no temporary store for old target 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* _That_impl_move = static_cast(_Other._Data._Impl)->_Move; + const auto* _This_impl_destroy = static_cast(_Data._Impl)->_Destroy; + + if (!_That_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 + _That_impl_move(_Data, _Other._Data); + } else { + // General case involving a temporary + _Move_only_function_data _Tmp; + _Move(_Tmp, _Data); + _That_impl_move(_Data, _Other._Data); + _This_impl_destroy(_Tmp); + } _Other._Reset_to_null(); } @@ -1751,7 +1770,6 @@ public: } move_only_function& operator=(move_only_function&& _Other) { - // GH-2278 - not implementing using swap as Standard asks for if (this != _STD addressof(_Other)) { this->_Move_assign(_STD move(_Other)); } @@ -1760,7 +1778,6 @@ public: template move_only_function& operator=(_Fn&& _Callable) { - // GH-2278 - not implementing using swap as Standard asks for this->_Move_assign(move_only_function(_STD forward<_Fn>(_Callable))); return *this; } From 5639bd870755c69e50d21fca4577c7d773feb2bd Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 16 Oct 2021 21:28:22 +0300 Subject: [PATCH 064/112] const --- stl/inc/functional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 29d9b23f2cc..5aae3be30bf 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1405,8 +1405,8 @@ public: // It is more efficient to do the reverse - this way no temporary store for old target 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* _That_impl_move = static_cast(_Other._Data._Impl)->_Move; - const auto* _This_impl_destroy = static_cast(_Data._Impl)->_Destroy; + auto* _That_impl_move = static_cast(_Other._Data._Impl)->_Move; + auto* _This_impl_destroy = static_cast(_Data._Impl)->_Destroy; if (!_That_impl_move) { // Move is trivial, destroy first if needed From 753d29bf74541bd4ccdf4b16c93c3f6d59876c4c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 17 Oct 2021 13:42:12 +0300 Subject: [PATCH 065/112] fix move_only_function --- stl/inc/functional | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 5aae3be30bf..581b48f6df0 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1229,12 +1229,14 @@ union alignas(max_align_t) _Move_only_function_data { } template - _NODISCARD _Fn* _Small_fn_ptr() noexcept { - return static_cast<_Fn*>(_Buf_ptr<_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 - _NODISCARD _Fn* _Large_fn_ptr() noexcept { + _NODISCARD _Fn* _Large_fn_ptr() const noexcept { return static_cast<_Fn*>(_Pointers[1]); } @@ -1244,17 +1246,17 @@ union alignas(max_align_t) _Move_only_function_data { }; template -[[noreturn]] _Rx _Function_no_callable(_Move_only_function_data&, _Types&&...) noexcept { +[[noreturn]] _Rx _Function_no_callable(const _Move_only_function_data&, _Types&&...) noexcept { _CSTD abort(); // We are not std::function to throw bad_function_call } template -_NODISCARD inline _Rx _Function_invoke_small(_Move_only_function_data& _Self, _Types&&... _Args) { +_NODISCARD inline _Rx _Function_invoke_small(const _Move_only_function_data& _Self, _Types&&... _Args) { return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } template -_NODISCARD inline _Rx _Function_invoke_large(_Move_only_function_data& _Self, _Types&&... _Args) { +_NODISCARD inline _Rx _Function_invoke_large(const _Move_only_function_data& _Self, _Types&&... _Args) { return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } @@ -1339,7 +1341,7 @@ public: // we don't have distinct wrapper class type for each callable type, only distinct functions when needed) // Calls target - _Rx (*_Invoke)(_Move_only_function_data&, _Types&&...); + _Rx (*_Invoke)(const _Move_only_function_data&, _Types&&...); // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") void (*_Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR; // Destroys data (not resetting its "vtable") From 1015cc11dc86010d870d9cce6ad77e37ffe104db Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 17 Oct 2021 13:43:22 +0300 Subject: [PATCH 066/112] coverage for pointers, const, noexcept --- .../tests/P0288R9_move_only_function/test.cpp | 123 +++++++++++++++--- 1 file changed, 108 insertions(+), 15 deletions(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index cf80a52d68a..b8adcd15bfe 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -158,8 +158,10 @@ void test_construct_impl(int expect, Args... args) { } test_function_t constructed_in_place(in_place_type, args...); assert(constructed_in_place(23, x) == expect); - assert(counter::copies == 0); - assert(counter::moves == 0); + if constexpr (is_class_v) { + assert(counter::copies == 0); + assert(counter::moves == 0); + } } if constexpr (is_class_v) { @@ -167,12 +169,6 @@ void test_construct_impl(int expect, Args... args) { } } -#ifdef __clang__ -// deliberate self-move as a test case -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wself-move" -#endif - void test_assign() { pass_this_by_ref x{63}; @@ -183,8 +179,6 @@ void test_assign() { assert(f2(23, x) == 38); f1 = large_callable{}; assert(f1(23, x) == 39); - f1 = std::move(f1); - assert(f1(23, x) == 39); } { @@ -194,8 +188,6 @@ void test_assign() { assert(f2(23, x) == 39); f1 = small_callable{}; assert(f1(23, x) == 38); - f1 = std::move(f1); - assert(f1(23, x) == 38); } { @@ -215,11 +207,23 @@ void test_assign() { 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 = std::move(f1); // deliberate self-move as a test case + assert(f1(23, x) == 38); + f2 = std::move(f2); // deliberate self-move as a test case + assert(f2(23, x) == 39); + } +#ifdef __clang__ #pragma clang diagnostic pop -#endif +#endif // __clang__ +} void test_swap() { pass_this_by_ref x{63}; @@ -257,7 +261,6 @@ void test_swap() { } } - void test_empty() { test_function_t emtpty; assert(!emtpty); @@ -271,6 +274,93 @@ void test_empty() { assert(emtpty_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; + } + }; + + std::move_only_function mem_fun_ptr(&s_t::f); + std::move_only_function mem_ptr(&s_t::j); + std::move_only_function 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); + + std::move_only_function mem_fun_ptr_n(static_cast(nullptr)); + std::move_only_function mem_ptr_n(static_cast(nullptr)); + std::move_only_function fun_ptr_n(static_cast(nullptr)); + + assert(!mem_fun_ptr_n); + assert(!mem_ptr_n); + assert(!fun_ptr_n); +} + +template +struct test_noexcept_t { + int operator()() noexcept(Nx) { + return 888; + } +}; + +void test_noexcept() { + using f_x = std::move_only_function; + using f_nx = std::move_only_function; + + static_assert(std::is_constructible_v>); + assert(f_x(test_noexcept_t{})() == 888); + + static_assert(std::is_constructible_v>); + assert(f_x(test_noexcept_t{})() == 888); + + static_assert(!std::is_constructible_v>); + + static_assert(std::is_constructible_v>); + assert(f_nx(test_noexcept_t{})() == 888); +} + +template +struct test_const_t { + int operator()() { + return 456; + } +}; + +template <> +struct test_const_t { + int operator()() const { + return 456; + } +}; + +void test_const() { + using f_c = std::move_only_function; + using f_nc = std::move_only_function; + + static_assert(std::is_constructible_v>); + assert(f_nc(test_const_t{})() == 456); + + static_assert(std::is_constructible_v>); + assert(f_nc(test_const_t{})() == 456); + + static_assert(!std::is_constructible_v>); + + static_assert(std::is_constructible_v>); + assert(f_c(test_const_t{})() == 456); +} + int main() { test_construct_impl(38); test_construct_impl(39); @@ -279,5 +369,8 @@ int main() { test_construct_impl(42, plain_callable); test_assign(); test_swap(); + test_ptr(); + test_noexcept(); + test_const(); test_empty(); } From 00d3c3071afa6da359772474d4a98fd9c9635baf Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 17 Oct 2021 14:12:54 +0300 Subject: [PATCH 067/112] minor cleanup, self-swap test, noexcept test fix --- .../tests/P0288R9_move_only_function/test.cpp | 116 ++++++++++-------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index b8adcd15bfe..8fa6aa45792 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -64,7 +64,7 @@ struct small_callable : counter { }; struct large_callable : counter { - char data[large_function_size]; + char data[large_function_size] = {}; int operator()(int a, pass_this_by_ref& b) { assert(a == 23); @@ -96,7 +96,7 @@ struct odd_cc_callable : counter { }; struct large_implicit_ptr_callable : counter { - char data[large_function_size]; + char data[large_function_size] = {}; using pfn = int (*)(int a, pass_this_by_ref& b); @@ -134,13 +134,10 @@ void test_construct_impl(int expect, Args... args) { assert(constructed_directly); assert(constructed_directly != nullptr); - test_function_t move_constructed = std::move(constructed_directly); + test_function_t move_constructed = move(constructed_directly); assert(move_constructed(23, x) == expect); - assert(!constructed_directly); - assert(constructed_directly == nullptr); - if constexpr (is_class_v) { assert(counter::copies == 0); } @@ -175,7 +172,7 @@ void test_assign() { { test_function_t f1{small_callable{}}; test_function_t f2{large_callable{}}; - f2 = std::move(f1); + f2 = move(f1); assert(f2(23, x) == 38); f1 = large_callable{}; assert(f1(23, x) == 39); @@ -184,7 +181,7 @@ void test_assign() { { test_function_t f1{large_callable{}}; test_function_t f2{small_callable{}}; - f2 = std::move(f1); + f2 = move(f1); assert(f2(23, x) == 39); f1 = small_callable{}; assert(f1(23, x) == 38); @@ -193,7 +190,7 @@ void test_assign() { { test_function_t f1{small_callable{}}; test_function_t f2{odd_cc_callable{}}; - f2 = std::move(f1); + f2 = move(f1); assert(f2(23, x) == 38); f1 = odd_cc_callable{}; assert(f1(23, x) == 40); @@ -202,7 +199,7 @@ void test_assign() { { test_function_t f1{large_callable{}}; test_function_t f2{large_implicit_ptr_callable{}}; - f2 = std::move(f1); + f2 = move(f1); assert(f2(23, x) == 39); f1 = large_implicit_ptr_callable{}; assert(f1(23, x) == 41); @@ -215,9 +212,11 @@ void test_assign() { { test_function_t f1{small_callable{}}; test_function_t f2{large_callable{}}; - f1 = std::move(f1); // deliberate self-move as a test case + 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 = std::move(f2); // deliberate self-move as a test case + 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__ @@ -231,23 +230,15 @@ void test_swap() { { test_function_t f1{small_callable{}}; test_function_t f2{large_callable{}}; - std::swap(f1, f2); + swap(f1, f2); assert(f2(23, x) == 38); assert(f1(23, x) == 39); } - { - test_function_t f1{large_callable{}}; - test_function_t f2{small_callable{}}; - f2.swap(f1); - assert(f2(23, x) == 39); - assert(f1(23, x) == 38); - } - { test_function_t f1{small_callable{}}; test_function_t f2{odd_cc_callable{}}; - swap(f1, f2); + f1.swap(f2); assert(f2(23, x) == 38); assert(f1(23, x) == 40); } @@ -255,23 +246,32 @@ void test_swap() { { test_function_t f1{large_callable{}}; test_function_t f2{large_implicit_ptr_callable{}}; - swap(f1, f2); + 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 emtpty; - assert(!emtpty); - assert(emtpty == nullptr); - assert(nullptr == emtpty); - - test_function_t emtpty_moved = std::move(emtpty); - assert(!emtpty_moved); - assert(emtpty_moved == nullptr); - assert(!emtpty_moved); - assert(emtpty_moved == nullptr); + test_function_t empty; + assert(!empty); + assert(empty == nullptr); + assert(nullptr == empty); + + test_function_t empty_moved = move(empty); + assert(!empty_moved); + assert(empty_moved == nullptr); + assert(!empty_moved); + assert(empty_moved == nullptr); } void test_ptr() { @@ -287,9 +287,9 @@ void test_ptr() { } }; - std::move_only_function mem_fun_ptr(&s_t::f); - std::move_only_function mem_ptr(&s_t::j); - std::move_only_function fun_ptr(&s_t::g); + move_only_function mem_fun_ptr(&s_t::f); + move_only_function mem_ptr(&s_t::j); + move_only_function fun_ptr(&s_t::g); s_t s; assert(mem_fun_ptr); @@ -299,9 +299,9 @@ void test_ptr() { assert(fun_ptr); assert(fun_ptr(34) == 31); - std::move_only_function mem_fun_ptr_n(static_cast(nullptr)); - std::move_only_function mem_ptr_n(static_cast(nullptr)); - std::move_only_function fun_ptr_n(static_cast(nullptr)); + move_only_function mem_fun_ptr_n(static_cast(nullptr)); + move_only_function mem_ptr_n(static_cast(nullptr)); + move_only_function fun_ptr_n(static_cast(nullptr)); assert(!mem_fun_ptr_n); assert(!mem_ptr_n); @@ -316,18 +316,21 @@ struct test_noexcept_t { }; void test_noexcept() { - using f_x = std::move_only_function; - using f_nx = std::move_only_function; + using f_x = move_only_function; + using f_nx = move_only_function; + + static_assert(!noexcept(declval()())); + static_assert(noexcept(declval()())); - static_assert(std::is_constructible_v>); + static_assert(is_constructible_v>); assert(f_x(test_noexcept_t{})() == 888); - static_assert(std::is_constructible_v>); + static_assert(is_constructible_v>); assert(f_x(test_noexcept_t{})() == 888); - static_assert(!std::is_constructible_v>); + static_assert(!is_constructible_v>); - static_assert(std::is_constructible_v>); + static_assert(is_constructible_v>); assert(f_nx(test_noexcept_t{})() == 888); } @@ -346,19 +349,24 @@ struct test_const_t { }; void test_const() { - using f_c = std::move_only_function; - using f_nc = std::move_only_function; + using f_c = move_only_function; + using f_nc = move_only_function; - static_assert(std::is_constructible_v>); - assert(f_nc(test_const_t{})() == 456); + static_assert(is_constructible_v>); + f_nc f1(test_const_t{}); + assert(f1() == 456); - static_assert(std::is_constructible_v>); - assert(f_nc(test_const_t{})() == 456); + static_assert(is_constructible_v>); + f_nc f2(test_const_t{}); + assert(f2() == 456); - static_assert(!std::is_constructible_v>); + static_assert(!is_constructible_v>); - static_assert(std::is_constructible_v>); - assert(f_c(test_const_t{})() == 456); + static_assert(is_constructible_v>); + f_c f3(test_const_t{}); + assert(f3() == 456); + const f_c f4(test_const_t{}); + assert(f4() == 456); } int main() { From 69d9b66ab1dc5f69ba7b8e657d54f647f65c9137 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 17 Oct 2021 14:54:47 +0300 Subject: [PATCH 068/112] __cpp_noexcept_function_type in test --- tests/std/tests/P0288R9_move_only_function/test.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 8fa6aa45792..dd787d8387b 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -320,7 +320,11 @@ void test_noexcept() { using f_nx = move_only_function; static_assert(!noexcept(declval()())); +#ifdef __cpp_noexcept_function_type static_assert(noexcept(declval()())); +#else // ^^^ defined(__cpp_noexcept_function_type) ^^^ / vvv !defined(__cpp_noexcept_function_type) vvv + static_assert(!noexcept(declval()())); +#endif // ^^^ !defined(__cpp_noexcept_function_type) ^^^ static_assert(is_constructible_v>); assert(f_x(test_noexcept_t{})() == 888); @@ -328,7 +332,12 @@ void test_noexcept() { static_assert(is_constructible_v>); assert(f_x(test_noexcept_t{})() == 888); +#ifdef __cpp_noexcept_function_type static_assert(!is_constructible_v>); +#else // ^^^ defined(__cpp_noexcept_function_type) ^^^ / vvv !defined(__cpp_noexcept_function_type) vvv + static_assert(is_constructible_v>); + assert(f_nx(test_noexcept_t{})() == 888); +#endif // ^^^ !defined(__cpp_noexcept_function_type) ^^^ static_assert(is_constructible_v>); assert(f_nx(test_noexcept_t{})() == 888); From 3a0ec41722cdc50850a74d1b2210b7a9da34730c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 17 Oct 2021 15:25:58 +0300 Subject: [PATCH 069/112] clang format --- stl/inc/functional | 2 ++ tests/std/tests/P0288R9_move_only_function/test.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 581b48f6df0..c6a7154b351 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1727,9 +1727,11 @@ public: 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 , int> = 0> diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index dd787d8387b..e93ead32247 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -213,7 +213,7 @@ void test_assign() { 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 +#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 From ec120ed618e2404fe83030ebbbefbd8b3c014b9c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 17 Oct 2021 16:10:56 +0300 Subject: [PATCH 070/112] this-> --- stl/inc/functional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index c6a7154b351..68fc50b5f07 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1302,7 +1302,7 @@ inline void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_fu } template -inline constexpr size_t _Function_small_copy_size = // We copy Impl* and and functor data at once +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 @@ -1769,7 +1769,7 @@ public: } move_only_function& operator=(nullptr_t) noexcept { - _Destroy(this->_Data); + this->_Destroy(this->_Data); this->Reset_to_null(); } From 407708350ba4b1949ed9b9363eec526da505e7fe Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sun, 17 Oct 2021 19:32:59 +0300 Subject: [PATCH 071/112] missing enable_if_t arguments --- stl/inc/functional | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 68fc50b5f07..6b248aa95e3 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1750,7 +1750,7 @@ public: this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_Fn>(_Callable)); } - template , int> = 0> + template , 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>); @@ -1759,7 +1759,8 @@ public: this->template _Construct_with_fn<_Vt, _VtInvQuals>(_STD forward<_CTypes>(_Args)...); } - template , int> = 0> + template , 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>); From 8bf4be01eb509d760a8f155b0ef7fb766222f295 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 22 Oct 2021 11:23:48 +0300 Subject: [PATCH 072/112] constrain assignment Co-authored-by: frederick-vs-ja --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 9f046785503..9b8ff851c58 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1779,7 +1779,7 @@ public: return *this; } - template + template , int> = 0> move_only_function& operator=(_Fn&& _Callable) { this->_Move_assign(move_only_function(_STD forward<_Fn>(_Callable))); return *this; From 8c09a1e371919067c7eda05c075993c188384600 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 22 Oct 2021 14:33:23 +0300 Subject: [PATCH 073/112] Imperfect forwarding --- stl/inc/functional | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 9b8ff851c58..f6af6f4f9b1 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1511,7 +1511,7 @@ public: static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt, _Types...>&& is_invocable_r_v<_Rx, _Vt&, _Types...>; - _Rx operator()(_Types&&... _Args) { + _Rx operator()(_Types... _Args) { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1527,7 +1527,7 @@ public: template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&, _Types...>; - _Rx operator()(_Types&&... _Args) & { + _Rx operator()(_Types... _Args) & { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1543,7 +1543,7 @@ public: template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&&, _Types...>; - _Rx operator()(_Types&&... _Args) && { + _Rx operator()(_Types... _Args) && { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1560,7 +1560,7 @@ public: static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt const, _Types...>&& is_invocable_r_v<_Rx, _Vt const&, _Types...>; - _Rx operator()(_Types&&... _Args) const { + _Rx operator()(_Types... _Args) const { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1576,7 +1576,7 @@ public: template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt const&, _Types...>; - _Rx operator()(_Types&&... _Args) const& { + _Rx operator()(_Types... _Args) const& { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1592,7 +1592,7 @@ public: template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt const&&, _Types...>; - _Rx operator()(_Types&&... _Args) const&& { + _Rx operator()(_Types... _Args) const&& { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1610,7 +1610,7 @@ public: 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 { + _Rx operator()(_Types... _Args) noexcept { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1626,7 +1626,7 @@ public: template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>; - _Rx operator()(_Types&&... _Args) & noexcept { + _Rx operator()(_Types... _Args) & noexcept { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1642,7 +1642,7 @@ public: template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&&, _Types...>; - _Rx operator()(_Types&&... _Args) && noexcept { + _Rx operator()(_Types... _Args) && noexcept { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1659,7 +1659,7 @@ public: static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt const, _Types...>&& is_nothrow_invocable_r_v<_Rx, _Vt const&, _Types...>; - _Rx operator()(_Types&&... _Args) const noexcept { + _Rx operator()(_Types... _Args) const noexcept { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1675,7 +1675,7 @@ public: template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt const&, _Types...>; - _Rx operator()(_Types&&... _Args) const& noexcept { + _Rx operator()(_Types... _Args) const& noexcept { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; @@ -1691,7 +1691,7 @@ public: template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt const&&, _Types...>; - _Rx operator()(_Types&&... _Args) const&& noexcept { + _Rx operator()(_Types... _Args) const&& noexcept { return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); } }; From a5da1b3c11cf89c1287833cbcd6508922c4d50e8 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 18 Nov 2021 21:05:42 +0200 Subject: [PATCH 074/112] minor review comments --- stl/inc/functional | 148 ++++++++++-------- .../tests/P0288R9_move_only_function/test.cpp | 23 +-- 2 files changed, 93 insertions(+), 78 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index f6af6f4f9b1..92f35598da0 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1214,15 +1214,15 @@ union alignas(max_align_t) _Move_only_function_data { char _Data; // For aliasing template - static constexpr size_t _Buf_offset = ((alignof(_Fn) <= sizeof(_Impl)) - ? sizeof(_Impl) // Data immediately after impl - : alignof(_Fn)); // Pad to next alignment + 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 static constexpr size_t _Buf_size = sizeof(_Pointers) - _Buf_offset<_Fn>; template - _NODISCARD constexpr void* _Buf_ptr() noexcept { + _NODISCARD void* _Buf_ptr() noexcept { return &_Data + _Buf_offset<_Fn>; } @@ -1243,34 +1243,40 @@ union alignas(max_align_t) _Move_only_function_data { } }; +// Size of large funtion. Treat empty function as if has this size. +// Treat small function as if has this size too if fits and trivially copyable. +inline constexpr size_t _Minimum_function_size = 2 * sizeof(void*); + template -[[noreturn]] _Rx _Function_no_callable(const _Move_only_function_data&, _Types&&...) noexcept { - _CSTD abort(); // We are not std::function to throw bad_function_call +[[noreturn]] _Rx _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 -_NODISCARD inline _Rx _Function_invoke_small(const _Move_only_function_data& _Self, _Types&&... _Args) { +_NODISCARD _Rx _Function_invoke_small(const _Move_only_function_data& _Self, _Types&&... _Args) { return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } template -_NODISCARD inline _Rx _Function_invoke_large(const _Move_only_function_data& _Self, _Types&&... _Args) { +_NODISCARD _Rx _Function_invoke_large(const _Move_only_function_data& _Self, _Types&&... _Args) { return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } template -inline void _Function_move_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { - ::new (_Self._Buf_ptr<_Vt>()) _Vt(_STD move(*_Src._Small_fn_ptr<_Vt>())); - _Src._Small_fn_ptr<_Vt>()->~_Vt(); +void _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; } inline void _Function_move_large(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { - _CSTD memcpy(&_Self._Data, &_Src._Data, 2 * sizeof(void*)); // Copy Impl* and functor data + _CSTD memcpy(&_Self._Data, &_Src._Data, _Minimum_function_size); // Copy Impl* and functor data } template -inline void _Function_destroy_small(_Move_only_function_data& _Self) noexcept { +void _Function_destroy_small(_Move_only_function_data& _Self) noexcept { _Self._Small_fn_ptr<_Vt>()->~_Vt(); } @@ -1279,13 +1285,13 @@ inline void _Function_deallocate_large_default_aligned(_Move_only_function_data& } template -inline void _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noexcept { +void _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noexcept { ::operator delete (_Self._Large_fn_ptr(), align_val_t{_Align}); } template -inline void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { - auto* _Pfn = _Self._Large_fn_ptr<_Vt>(); +void _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(_Pfn)); @@ -1295,7 +1301,7 @@ inline void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { } template -inline void _Function_move_memcpy(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { +void _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 } @@ -1305,11 +1311,13 @@ inline constexpr size_t _Function_small_copy_size = // We copy Impl* and the fun (size_t{sizeof(_Vt) + sizeof(void*) - 1} & ~size_t{sizeof(void*) - 1}); // size in whole pointers template -_NODISCARD inline void* _Function_new_large(_CTypes&&... _Args) { +_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); @@ -1333,21 +1341,21 @@ _NODISCARD inline void* _Function_new_large(_CTypes&&... _Args) { template class _Move_only_function_base { public: - struct _Impl_t { // per-callable-type structure acting as a virtual function table - // using vtable emulations gives more flexibility for optimizations and reduces the need for RTTIs - // (RTTI saving may be significant as with lambdas and binds there may be a lot of distinct callable types, - // we don't have distinct wrapper class type for each callable type, only distinct functions when needed) + 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 - _Rx (*_Invoke)(const _Move_only_function_data&, _Types&&...); + _Rx(__stdcall* _Invoke)(const _Move_only_function_data&, _Types&&...); // Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable") - void (*_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") - void (*_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_no_callable<_Rx, _Types...>, + _Function_not_callable<_Rx, _Types...>, nullptr, nullptr, }; @@ -1357,7 +1365,7 @@ public: _Move_only_function_base() noexcept = default; // leaves fields uninitialized _Move_only_function_base(_Move_only_function_base&& _Other) noexcept { - this->_Move(_Data, _Other._Data); + _Checked_move(_Data, _Other._Data); _Other._Reset_to_null(); } @@ -1381,34 +1389,35 @@ public: } ~_Move_only_function_base() { - _Destroy(_Data); + _Checked_destroy(_Data); } - static void _Destroy(_Move_only_function_data& _Data) { - const auto* _Impl = static_cast(_Data._Impl); + static void _Checked_destroy(_Move_only_function_data& _Data) noexcept { + const auto _Impl = static_cast(_Data._Impl); if (_Impl->_Destroy) { _Impl->_Destroy(_Data); } } - static void _Move(_Move_only_function_data& _Data, _Move_only_function_data& _Src) { - const auto* _Impl = static_cast(_Src._Impl); - if (!_Impl->_Move) { - _Function_move_large(_Data, _Src); - } else { + static void _Checked_move(_Move_only_function_data& _Data, _Move_only_function_data& _Src) noexcept { + const auto _Impl = static_cast(_Src._Impl); + if (_Impl->_Move) { _Impl->_Move(_Data, _Src); + } else { + _Function_move_large(_Data, _Src); } } - void _Move_assign(_Move_only_function_base&& _Other) { - // As specified in P0288R9, we excepted to first move the new target, then finally destroy the old target. - // It is more efficient to do the reverse - this way no temporary store for old target used. + 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. - auto* _That_impl_move = static_cast(_Other._Data._Impl)->_Move; - auto* _This_impl_destroy = static_cast(_Data._Impl)->_Destroy; + const auto _Other_impl_move = static_cast(_Other._Data._Impl)->_Move; + const auto _This_impl_destroy = static_cast(_Data._Impl)->_Destroy; - if (!_That_impl_move) { + if (!_Other_impl_move) { // Move is trivial, destroy first if needed if (_This_impl_destroy) { _This_impl_destroy(_Data); @@ -1416,23 +1425,23 @@ public: _Function_move_large(_Data, _Other._Data); } else if (!_This_impl_destroy) { // Destroy is trivial, just move - _That_impl_move(_Data, _Other._Data); + _Other_impl_move(_Data, _Other._Data); } else { // General case involving a temporary _Move_only_function_data _Tmp; - _Move(_Tmp, _Data); - _That_impl_move(_Data, _Other._Data); + _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) { + void _Swap(_Move_only_function_base& _Other) noexcept { _Move_only_function_data _Tmp; - _Move(_Tmp, _Data); - _Move(_Data, _Other._Data); - _Move(_Other._Data, _Tmp); + _Checked_move(_Tmp, _Data); + _Checked_move(_Data, _Other._Data); + _Checked_move(_Other._Data, _Tmp); } _NODISCARD bool _Is_null() const noexcept { @@ -1444,7 +1453,7 @@ public: 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 { + _NODISCARD auto _Get_invoke() const noexcept { return static_cast(_Data._Impl)->_Invoke; } @@ -1468,7 +1477,7 @@ public: _Impl._Invoke = _Function_invoke_small<_Vt, _VtInvQuals, _Rx, _Types...>; if constexpr (is_trivially_copyable_v<_Vt> && is_trivially_destructible_v<_Vt>) { - if constexpr ((_Function_small_copy_size<_Vt>) > sizeof(void*) * 2) { + if constexpr ((_Function_small_copy_size<_Vt>) > _Minimum_function_size) { _Impl._Move = _Function_move_memcpy<_Function_small_copy_size<_Vt>>; } else { _Impl._Move = nullptr; @@ -1493,10 +1502,11 @@ public: } }; -template +template class _Move_only_function_call { - static_assert(_Always_false<_Fn>, "std::move_only_function only accepts function types as template arguments, " - "with possibly const/ref/noexcept qualifiers."); + static_assert(_Always_false>, + "std::move_only_function only accepts function types as template arguments, " + "with possibly const/ref/noexcept qualifiers."); }; template @@ -1697,27 +1707,27 @@ public: }; #endif // __cpp_noexcept_function_type -template -class move_only_function : private _Move_only_function_call<_Fx> { +template +class move_only_function : private _Move_only_function_call<_Signature...> { private: - using _Call = _Move_only_function_call<_Fx>; + using _Call = _Move_only_function_call<_Signature...>; // clang-format off template 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>; + !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>; template static constexpr bool _Enable_in_place_constructor = - is_constructible_v, _CTypes...> && - _Call::template _Is_callable_from>; + is_constructible_v, _CTypes...> + && _Call::template _Is_callable_from>; template static constexpr bool _Enable_in_place_list_constructor = - is_constructible_v, initializer_list<_Ux>, _CTypes...> && - _Call::template _Is_callable_from>; + is_constructible_v, initializer_list<_Ux>&, _CTypes...> + && _Call::template _Is_callable_from>; // clang-format on public: using typename _Call::result_type; @@ -1735,9 +1745,10 @@ public: template , int> = 0> move_only_function(_Fn&& _Callable) { using _Vt = decay_t<_Fn>; - static_assert(is_constructible_v<_Vt, _Fn>); + static_assert(is_constructible_v<_Vt, _Fn>, "_Vt should be constructible from _Fn. " + "(N4901 [func.wrap.move.ctor]/6)"); - if constexpr (is_member_function_pointer_v<_Vt> || is_member_object_pointer_v<_Vt> || is_pointer_v<_Vt>) { + 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; @@ -1751,7 +1762,7 @@ public: template , 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>); + 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)...); @@ -1761,7 +1772,7 @@ public: 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>); + 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)...); @@ -1770,6 +1781,7 @@ public: move_only_function& operator=(nullptr_t) noexcept { this->_Destroy(this->_Data); this->Reset_to_null(); + return *this; } move_only_function& operator=(move_only_function&& _Other) { @@ -1781,7 +1793,7 @@ public: template , int> = 0> move_only_function& operator=(_Fn&& _Callable) { - this->_Move_assign(move_only_function(_STD forward<_Fn>(_Callable))); + this->_Move_assign(move_only_function{_STD forward<_Fn>(_Callable)}); return *this; } diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index e93ead32247..04e031651be 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include +#include #include +#include +#include using namespace std; @@ -262,16 +265,16 @@ void test_swap() { } void test_empty() { - test_function_t empty; - assert(!empty); - assert(empty == nullptr); - assert(nullptr == empty); - - test_function_t empty_moved = move(empty); - assert(!empty_moved); - assert(empty_moved == nullptr); - assert(!empty_moved); - assert(empty_moved == nullptr); + 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); + assert(!no_callable); + assert(no_callable == nullptr); + assert(!no_callable_moved); + assert(no_callable_moved == nullptr); } void test_ptr() { From 28e7e841e8cf37cb5d144dfcf64ddf0dd1687e54 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 18 Nov 2021 21:13:34 +0200 Subject: [PATCH 075/112] __stdcall everywhere! --- stl/inc/functional | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 92f35598da0..f61136aeef7 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1248,49 +1248,54 @@ union alignas(max_align_t) _Move_only_function_data { inline constexpr size_t _Minimum_function_size = 2 * sizeof(void*); template -[[noreturn]] _Rx _Function_not_callable(const _Move_only_function_data&, _Types&&...) noexcept { +[[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 -_NODISCARD _Rx _Function_invoke_small(const _Move_only_function_data& _Self, _Types&&... _Args) { +_NODISCARD _Rx __stdcall _Function_invoke_small(const _Move_only_function_data& _Self, _Types&&... _Args) { return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } template -_NODISCARD _Rx _Function_invoke_large(const _Move_only_function_data& _Self, _Types&&... _Args) { +_NODISCARD _Rx __stdcall _Function_invoke_large(const _Move_only_function_data& _Self, _Types&&... _Args) { return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } template -void _Function_move_small(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { +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; } -inline void _Function_move_large(_Move_only_function_data& _Self, _Move_only_function_data& _Src) noexcept { +template +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 -void _Function_destroy_small(_Move_only_function_data& _Self) noexcept { +void __stdcall _Function_destroy_small(_Move_only_function_data& _Self) noexcept { _Self._Small_fn_ptr<_Vt>()->~_Vt(); } -inline void _Function_deallocate_large_default_aligned(_Move_only_function_data& _Self) noexcept { +inline void __stdcall _Function_deallocate_large_default_aligned(_Move_only_function_data& _Self) noexcept { ::operator delete(_Self._Large_fn_ptr()); } template -void _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noexcept { +void __stdcall _Function_deallocate_large_overaligned(_Move_only_function_data& _Self) noexcept { ::operator delete (_Self._Large_fn_ptr(), align_val_t{_Align}); } template -void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { +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__) { @@ -1300,11 +1305,6 @@ void _Function_destroy_large(_Move_only_function_data& _Self) noexcept { } } -template -void _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 -} - template 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 From 2210aba64cb131fde53a93de6ca36482ce7d5ecb Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 18 Nov 2021 21:18:26 +0200 Subject: [PATCH 076/112] ... --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index f61136aeef7..e83ec134370 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1504,7 +1504,7 @@ public: template class _Move_only_function_call { - static_assert(_Always_false>, + static_assert(_Always_false>, "std::move_only_function only accepts function types as template arguments, " "with possibly const/ref/noexcept qualifiers."); }; From dcdb395ff22204720e196a1dddcaf563384c129a Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 18 Nov 2021 21:34:55 +0200 Subject: [PATCH 077/112] _Remove _Remove_cvref_t --- stl/inc/functional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index e83ec134370..abeb87a4427 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1715,8 +1715,8 @@ private: // clang-format off template 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> + !is_same_v, move_only_function> + && !_Is_specialization_v, in_place_type_t> && _Call::template _Is_callable_from>; template From 80a28af22e45595ec81e6d0cfe03ee15f0e4b70b Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 18 Nov 2021 21:53:54 +0200 Subject: [PATCH 078/112] Thanks @CaseyCarter and @tcanens --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index abeb87a4427..5277a21eb4f 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1504,7 +1504,7 @@ public: template class _Move_only_function_call { - static_assert(_Always_false>, + static_assert((_Always_false<_Signature> || ...), "std::move_only_function only accepts function types as template arguments, " "with possibly const/ref/noexcept qualifiers."); }; From 5241aa3e5e3ca2e314effaa174f7460ce865d3f2 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 09:14:14 +0200 Subject: [PATCH 079/112] Your friendly CTAD is not helping you here --- stl/inc/functional | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stl/inc/functional b/stl/inc/functional index 5277a21eb4f..69665a4a7d1 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1507,6 +1507,9 @@ 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."); }; template From 9d4f688676b70842af5f3a241c25ec7b43c8643e Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 19:05:02 +0200 Subject: [PATCH 080/112] const west, no rvalue ref for is_invokable_r_v all generated by script --- stl/inc/functional | 115 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 101 insertions(+), 14 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 69665a4a7d1..39b75f6ae1c 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1512,6 +1512,69 @@ class _Move_only_function_call { "Unlike std::function, std::move_only_function does not define class template argument deduction guides."); }; +// A script to generate the specializations below. +// Avoiding C++ preprocessor for better IDE navigation and debugging experience. +#if 0 +#include +#include +#include + +int main() { + constexpr char inst[] = R"--( + template + class _Move_only_function_call<_Rx(_Types...) {0} {1} {3}> : public _Move_only_function_base<_Rx, _Types...> {{ + public: + using result_type = _Rx; + + template + using _VtInvQuals = {0} _Vt {2}; + + static constexpr bool _Noexcept = {4}; + + template + static constexpr bool _Is_callable_from = {5}; + + _Rx operator()(_Types... _Args) {0} {1} {3} {{ + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); + }} + }}; +)--"; + + const char* ref_str[] = { "", "&", "&&" }; + + for (int noex : {0, 1}) { + if (noex) { + std::cout << "#ifdef __cpp_noexcept_function_type\n"; + } + + for (const char* cv : { "", "const" }) { + for (int ref : {0, 1, 2}) { + + std::string callable; + switch (ref) + { + case 0: callable = std::format("{0}<_Rx, {1} _Vt, _Types...> && {0}<_Rx, {1} _Vt&, _Types...>", + noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); + break; + case 1: callable = std::format("{0}<_Rx, {1} _Vt&, _Types...>", + noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); + break; + case 2: callable = std::format("{0}<_Rx, {1} _Vt, _Types...>", + noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); + break; + } + + std::cout << std::format(inst, cv, ref_str[ref], ref == 2 ? "&&" : "&", noex ? "noexcept" : "", noex ? "true" : "false", callable); + } + } + + if (noex) { + std::cout << "#endif // __cpp_noexcept_function_type\n"; + } + } +} +#endif + template class _Move_only_function_call<_Rx(_Types...)> : public _Move_only_function_base<_Rx, _Types...> { public: @@ -1520,6 +1583,8 @@ public: template using _VtInvQuals = _Vt&; + static constexpr bool _Noexcept = false; + template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt, _Types...>&& is_invocable_r_v<_Rx, _Vt&, _Types...>; @@ -1537,6 +1602,8 @@ public: template using _VtInvQuals = _Vt&; + static constexpr bool _Noexcept = false; + template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&, _Types...>; @@ -1553,8 +1620,10 @@ public: template using _VtInvQuals = _Vt&&; + static constexpr bool _Noexcept = false; + template - static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&&, _Types...>; + 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)...); @@ -1567,11 +1636,13 @@ public: using result_type = _Rx; template - using _VtInvQuals = _Vt const&; + using _VtInvQuals = const _Vt&; + + static constexpr bool _Noexcept = false; template static constexpr bool _Is_callable_from = - is_invocable_r_v<_Rx, _Vt const, _Types...>&& is_invocable_r_v<_Rx, _Vt const&, _Types...>; + 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)...); @@ -1584,10 +1655,12 @@ public: using result_type = _Rx; template - using _VtInvQuals = _Vt const&; + using _VtInvQuals = const _Vt&; + + static constexpr bool _Noexcept = false; template - static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt const&, _Types...>; + 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)...); @@ -1600,10 +1673,12 @@ public: using result_type = _Rx; template - using _VtInvQuals = _Vt const&&; + using _VtInvQuals = const _Vt&&; + + static constexpr bool _Noexcept = false; template - static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt const&&, _Types...>; + 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)...); @@ -1619,6 +1694,8 @@ public: template using _VtInvQuals = _Vt&; + static constexpr bool _Noexcept = true; + template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt, _Types...>&& is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>; @@ -1636,6 +1713,8 @@ public: template using _VtInvQuals = _Vt&; + static constexpr bool _Noexcept = true; + template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>; @@ -1652,8 +1731,10 @@ public: template using _VtInvQuals = _Vt&&; + static constexpr bool _Noexcept = true; + template - static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&&, _Types...>; + 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)...); @@ -1666,11 +1747,13 @@ public: using result_type = _Rx; template - using _VtInvQuals = _Vt const&; + using _VtInvQuals = const _Vt&; + + static constexpr bool _Noexcept = true; template static constexpr bool _Is_callable_from = - is_nothrow_invocable_r_v<_Rx, _Vt const, _Types...>&& is_nothrow_invocable_r_v<_Rx, _Vt const&, _Types...>; + 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)...); @@ -1683,10 +1766,12 @@ public: using result_type = _Rx; template - using _VtInvQuals = _Vt const&; + using _VtInvQuals = const _Vt&; + + static constexpr bool _Noexcept = true; template - static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt const&, _Types...>; + 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)...); @@ -1699,10 +1784,12 @@ public: using result_type = _Rx; template - using _VtInvQuals = _Vt const&&; + using _VtInvQuals = const _Vt&&; + + static constexpr bool _Noexcept = true; template - static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt const&&, _Types...>; + 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)...); From e04ecc65a3f8b5c9801295f8cf22ec08862b9c5d Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 19:18:26 +0200 Subject: [PATCH 081/112] untab --- stl/inc/functional | 93 +++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 39b75f6ae1c..7b3d1234fef 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1520,58 +1520,59 @@ class _Move_only_function_call { #include int main() { - constexpr char inst[] = R"--( - template - class _Move_only_function_call<_Rx(_Types...) {0} {1} {3}> : public _Move_only_function_base<_Rx, _Types...> {{ - public: - using result_type = _Rx; + constexpr char inst[] = R"--( + template + class _Move_only_function_call<_Rx(_Types...) {0} {1} {3}> : public _Move_only_function_base<_Rx, _Types...> {{ + public: + using result_type = _Rx; - template - using _VtInvQuals = {0} _Vt {2}; + template + using _VtInvQuals = {0} _Vt {2}; - static constexpr bool _Noexcept = {4}; + static constexpr bool _Noexcept = {4}; - template - static constexpr bool _Is_callable_from = {5}; + template + static constexpr bool _Is_callable_from = {5}; - _Rx operator()(_Types... _Args) {0} {1} {3} {{ - return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); - }} - }}; + _Rx operator()(_Types... _Args) {0} {1} {3} {{ + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); + }} + }}; )--"; - const char* ref_str[] = { "", "&", "&&" }; - - for (int noex : {0, 1}) { - if (noex) { - std::cout << "#ifdef __cpp_noexcept_function_type\n"; - } - - for (const char* cv : { "", "const" }) { - for (int ref : {0, 1, 2}) { - - std::string callable; - switch (ref) - { - case 0: callable = std::format("{0}<_Rx, {1} _Vt, _Types...> && {0}<_Rx, {1} _Vt&, _Types...>", - noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); - break; - case 1: callable = std::format("{0}<_Rx, {1} _Vt&, _Types...>", - noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); - break; - case 2: callable = std::format("{0}<_Rx, {1} _Vt, _Types...>", - noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); - break; - } - - std::cout << std::format(inst, cv, ref_str[ref], ref == 2 ? "&&" : "&", noex ? "noexcept" : "", noex ? "true" : "false", callable); - } - } - - if (noex) { - std::cout << "#endif // __cpp_noexcept_function_type\n"; - } - } + const char* ref_str[] = { "", "&", "&&" }; + + for (int noex : {0, 1}) { + if (noex) { + std::cout << "#ifdef __cpp_noexcept_function_type\n"; + } + + for (const char* cv : { "", "const" }) { + for (int ref : {0, 1, 2}) { + + std::string callable; + switch (ref) + { + case 0: callable = std::format("{0}<_Rx, {1} _Vt, _Types...> && {0}<_Rx, {1} _Vt&, _Types...>", + noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); + break; + case 1: callable = std::format("{0}<_Rx, {1} _Vt&, _Types...>", + noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); + break; + case 2: callable = std::format("{0}<_Rx, {1} _Vt, _Types...>", + noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); + break; + } + + std::cout << std::format(inst, cv, ref_str[ref], ref == 2 ? "&&" : "&", + noex ? "noexcept" : "", noex ? "true" : "false", callable); + } + } + + if (noex) { + std::cout << "#endif // __cpp_noexcept_function_type\n"; + } + } } #endif From 081d8762bb4a0a2f3d51e65db94e42493906f12e Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 19:22:58 +0200 Subject: [PATCH 082/112] -ws --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 7b3d1234fef..ac17c9657b6 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1564,7 +1564,7 @@ int main() { break; } - std::cout << std::format(inst, cv, ref_str[ref], ref == 2 ? "&&" : "&", + std::cout << std::format(inst, cv, ref_str[ref], ref == 2 ? "&&" : "&", noex ? "noexcept" : "", noex ? "true" : "false", callable); } } From 46579ba72dd048cdfa86552d771ad7359cfe8b5e Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 19:33:34 +0200 Subject: [PATCH 083/112] eol --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index ac17c9657b6..0a7aa51df90 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1564,7 +1564,7 @@ int main() { break; } - std::cout << std::format(inst, cv, ref_str[ref], ref == 2 ? "&&" : "&", + std::cout << std::format(inst, cv, ref_str[ref], ref == 2 ? "&&" : "&", noex ? "noexcept" : "", noex ? "true" : "false", callable); } } From 8414c3bec62e51880a6d5d95dbb0da8da6c1d8f6 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 19:46:17 +0200 Subject: [PATCH 084/112] noexcept invoke --- stl/inc/functional | 67 +++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 43 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 0a7aa51df90..bf8b6a63f7d 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1253,13 +1253,13 @@ template // (N4901 [func.wrap.move.inv]/2) } -template -_NODISCARD _Rx __stdcall _Function_invoke_small(const _Move_only_function_data& _Self, _Types&&... _Args) { +template +_NODISCARD _Rx __stdcall _Function_inv_small(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) { return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } -template -_NODISCARD _Rx __stdcall _Function_invoke_large(const _Move_only_function_data& _Self, _Types&&... _Args) { +template +_NODISCARD _Rx __stdcall _Function_inv_large(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) { return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); } @@ -1338,7 +1338,7 @@ _NODISCARD void* _Function_new_large(_CTypes&&... _Args) { return _STD exchange(_Guard._Ptr, nullptr); } -template +template class _Move_only_function_base { public: struct _Impl_t { // A per-callable-type structure acting as a virtual function table. @@ -1347,7 +1347,11 @@ public: // Here we don't have a distinct wrapper class for each callable type, only distinct functions when needed.) // Calls target +#ifdef __cpp_noexcept_function_type + _Rx(__stdcall* _Invoke)(const _Move_only_function_data&, _Types&&...) noexcept(_Noexcept); +#else _Rx(__stdcall* _Invoke)(const _Move_only_function_data&, _Types&&...); +#endif // 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") @@ -1461,7 +1465,7 @@ public: _NODISCARD static constexpr _Impl_t _Create_impl() noexcept { _Impl_t _Impl{}; if constexpr (_Large_function_engaged<_Vt>) { - _Impl._Invoke = _Function_invoke_large<_Vt, _VtInvQuals, _Rx, _Types...>; + _Impl._Invoke = _Function_inv_large<_Vt, _VtInvQuals, _Rx, _Noexcept, _Types...>; _Impl._Move = nullptr; if constexpr (is_trivially_destructible_v<_Vt>) { @@ -1474,7 +1478,7 @@ public: _Impl._Destroy = _Function_destroy_large<_Vt>; } } else { - _Impl._Invoke = _Function_invoke_small<_Vt, _VtInvQuals, _Rx, _Types...>; + _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) { @@ -1577,15 +1581,13 @@ int main() { #endif template -class _Move_only_function_call<_Rx(_Types...)> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...)> : public _Move_only_function_base<_Rx, false, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = _Vt&; - static constexpr bool _Noexcept = false; - template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt, _Types...>&& is_invocable_r_v<_Rx, _Vt&, _Types...>; @@ -1596,15 +1598,13 @@ public: }; template -class _Move_only_function_call<_Rx(_Types...)&> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...)&> : public _Move_only_function_base<_Rx, false, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = _Vt&; - static constexpr bool _Noexcept = false; - template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt&, _Types...>; @@ -1614,15 +1614,13 @@ public: }; template -class _Move_only_function_call<_Rx(_Types...) &&> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...) &&> : public _Move_only_function_base<_Rx, false, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = _Vt&&; - static constexpr bool _Noexcept = false; - template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, _Vt, _Types...>; @@ -1632,15 +1630,13 @@ public: }; template -class _Move_only_function_call<_Rx(_Types...) const> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...) const> : public _Move_only_function_base<_Rx, false, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = const _Vt&; - static constexpr bool _Noexcept = false; - template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, const _Vt, _Types...>&& is_invocable_r_v<_Rx, const _Vt&, _Types...>; @@ -1651,15 +1647,13 @@ public: }; template -class _Move_only_function_call<_Rx(_Types...) const&> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...) const&> : public _Move_only_function_base<_Rx, false, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = const _Vt&; - static constexpr bool _Noexcept = false; - template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, const _Vt&, _Types...>; @@ -1669,15 +1663,13 @@ public: }; template -class _Move_only_function_call<_Rx(_Types...) const&&> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...) const&&> : public _Move_only_function_base<_Rx, false, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = const _Vt&&; - static constexpr bool _Noexcept = false; - template static constexpr bool _Is_callable_from = is_invocable_r_v<_Rx, const _Vt, _Types...>; @@ -1688,15 +1680,13 @@ public: #ifdef __cpp_noexcept_function_type template -class _Move_only_function_call<_Rx(_Types...) noexcept> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...) noexcept> : public _Move_only_function_base<_Rx, true, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = _Vt&; - static constexpr bool _Noexcept = true; - template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt, _Types...>&& is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>; @@ -1707,15 +1697,13 @@ public: }; template -class _Move_only_function_call<_Rx(_Types...)& noexcept> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...)& noexcept> : public _Move_only_function_base<_Rx, true, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = _Vt&; - static constexpr bool _Noexcept = true; - template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt&, _Types...>; @@ -1725,15 +1713,13 @@ public: }; template -class _Move_only_function_call<_Rx(_Types...)&& noexcept> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...)&& noexcept> : public _Move_only_function_base<_Rx, true, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = _Vt&&; - static constexpr bool _Noexcept = true; - template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, _Vt, _Types...>; @@ -1743,15 +1729,13 @@ public: }; template -class _Move_only_function_call<_Rx(_Types...) const noexcept> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...) const noexcept> : public _Move_only_function_base<_Rx, true, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = const _Vt&; - static constexpr bool _Noexcept = true; - template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, const _Vt, _Types...>&& is_nothrow_invocable_r_v<_Rx, const _Vt&, _Types...>; @@ -1762,15 +1746,13 @@ public: }; template -class _Move_only_function_call<_Rx(_Types...) const& noexcept> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...) const& noexcept> : public _Move_only_function_base<_Rx, true, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = const _Vt&; - static constexpr bool _Noexcept = true; - template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, const _Vt&, _Types...>; @@ -1780,15 +1762,14 @@ public: }; template -class _Move_only_function_call<_Rx(_Types...) const&& noexcept> : public _Move_only_function_base<_Rx, _Types...> { +class _Move_only_function_call<_Rx(_Types...) const&& noexcept> + : public _Move_only_function_base<_Rx, true, _Types...> { public: using result_type = _Rx; template using _VtInvQuals = const _Vt&&; - static constexpr bool _Noexcept = true; - template static constexpr bool _Is_callable_from = is_nothrow_invocable_r_v<_Rx, const _Vt, _Types...>; From c178c6451f2458e216918816294cd51d89af389f Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 19:54:45 +0200 Subject: [PATCH 085/112] assign nullptr --- stl/inc/functional | 4 ++-- .../tests/P0288R9_move_only_function/test.cpp | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index bf8b6a63f7d..c3f81884fb7 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1851,8 +1851,8 @@ public: } move_only_function& operator=(nullptr_t) noexcept { - this->_Destroy(this->_Data); - this->Reset_to_null(); + this->_Checked_destroy(this->_Data); + this->_Reset_to_null(); return *this; } diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 04e031651be..f4c356b03ab 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -225,6 +225,27 @@ void test_assign() { #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() { From 76328f40253ccf0f02d8c2916d89f7b40dc15815 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 20:01:37 +0200 Subject: [PATCH 086/112] result_type --- tests/std/tests/P0288R9_move_only_function/test.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index f4c356b03ab..5638e03aba8 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -402,6 +402,12 @@ void test_const() { assert(f4() == 456); } +static_assert(is_same_v::result_type, void>); +static_assert(is_same_v::result_type, int>); +static_assert(is_same_v::result_type, int (*)(char*)>); +static_assert(is_same_v(long long&) const>::result_type, + move_only_function>); + int main() { test_construct_impl(38); test_construct_impl(39); From b8683a25a60507c22431c5f7157e0d91c25a866e Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 20:20:18 +0200 Subject: [PATCH 087/112] initializer list test --- .../tests/P0288R9_move_only_function/test.cpp | 62 +++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 5638e03aba8..81e9e14b537 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -332,6 +332,49 @@ void test_ptr() { assert(!fun_ptr_n); } +void test_inner() { + move_only_function f1(nullptr); + move_only_function f2 = move(f1); + assert(!f2); + f2 = move(f1); + assert(!f1); +} + + +void test_inplace_list() { + struct in_place_list_constructible { + in_place_list_constructible(std::initializer_list li) { + int x = 0; + for (int i : li) { + ++x; + assert(x == i); + } + } + + in_place_list_constructible(std::initializer_list 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 f1(in_place_type, {1, 2, 3, 4, 5}); + assert(f1(5) == 4); + + move_only_function f2(in_place_type, {-1, -2, -3, -4, -5}, "fox"); + assert(f2(8) == 7); +} + + template struct test_noexcept_t { int operator()() noexcept(Nx) { @@ -403,10 +446,20 @@ void test_const() { } static_assert(is_same_v::result_type, void>); -static_assert(is_same_v::result_type, int>); -static_assert(is_same_v::result_type, int (*)(char*)>); -static_assert(is_same_v(long long&) const>::result_type, - move_only_function>); +static_assert(is_same_v::result_type, short>); +static_assert(is_same_v::result_type, int>); +static_assert(is_same_v::result_type, void>); +static_assert(is_same_v::result_type, short>); +static_assert(is_same_v::result_type, int>); + +#ifdef __cpp_noexcept_function_type +static_assert(is_same_v::result_type, void>); +static_assert(is_same_v::result_type, short>); +static_assert(is_same_v::result_type, int>); +static_assert(is_same_v::result_type, void>); +static_assert(is_same_v::result_type, short>); +static_assert(is_same_v::result_type, int>); +#endif // ^^^ defined(__cpp_noexcept_function_type) ^^^ int main() { test_construct_impl(38); @@ -417,6 +470,7 @@ int main() { test_assign(); test_swap(); test_ptr(); + test_inner(); test_noexcept(); test_const(); test_empty(); From 9b944259d70577e7bd256458c0d2d4d960760f5a Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 20:36:46 +0200 Subject: [PATCH 088/112] noexcept workaround --- stl/inc/functional | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index c3f81884fb7..c424b17a7d9 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1341,23 +1341,33 @@ _NODISCARD void* _Function_new_large(_CTypes&&... _Args) { template class _Move_only_function_base { public: - struct _Impl_t { // A per-callable-type structure acting as a virtual function table. + template + struct _Impl_t_x { // 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 -#ifdef __cpp_noexcept_function_type - _Rx(__stdcall* _Invoke)(const _Move_only_function_data&, _Types&&...) noexcept(_Noexcept); -#else _Rx(__stdcall* _Invoke)(const _Move_only_function_data&, _Types&&...); -#endif // 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; }; + // Noexcept specialization + template <> + struct _Impl_t_x { + // Calls target + _Rx(__stdcall* _Invoke)(const _Move_only_function_data&, _Types&&...) _NOEXCEPT_FNPTR; + // 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; + }; + + using _Impl_t = _Impl_t_x<_Noexcept>; + static constexpr _Impl_t _Null_move_only_function = { _Function_not_callable<_Rx, _Types...>, nullptr, From da559bc4f4c215bd8618a911ca167238bcfbc620 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 20:37:07 +0200 Subject: [PATCH 089/112] avoid truncation --- tests/std/tests/P0288R9_move_only_function/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 81e9e14b537..afdbb6b8f39 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -333,8 +333,8 @@ void test_ptr() { } void test_inner() { - move_only_function f1(nullptr); - move_only_function f2 = move(f1); + move_only_function f1(nullptr); + move_only_function f2 = move(f1); assert(!f2); f2 = move(f1); assert(!f1); From f77ed3b49df4a623fda834f63e8d7f190f1ca77f Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 20:54:21 +0200 Subject: [PATCH 090/112] TRANSITION --- stl/inc/functional | 1 + 1 file changed, 1 insertion(+) diff --git a/stl/inc/functional b/stl/inc/functional index c424b17a7d9..b28bfa2b13c 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1356,6 +1356,7 @@ public: }; // Noexcept specialization + // TRANSITION, DevCom-1208330: use noexcept(_Noexcept) instead template <> struct _Impl_t_x { // Calls target From 18011ebba988133647359afb6ce9ebb84ee6eec3 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 21:37:08 +0200 Subject: [PATCH 091/112] `&` & `&&` --- stl/inc/functional | 6 ++-- .../tests/P0288R9_move_only_function/test.cpp | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index b28bfa2b13c..6f5965686da 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1255,12 +1255,14 @@ template template _NODISCARD _Rx __stdcall _Function_inv_small(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) { - return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); + return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>( + static_cast<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...); } template _NODISCARD _Rx __stdcall _Function_inv_large(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) { - return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>(), _STD forward<_Types>(_Args)...); + return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>( + static_cast<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...); } template diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index afdbb6b8f39..7b2c539c078 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -445,6 +445,37 @@ void test_const() { assert(f4() == 456); } + +void test_qual() { + move_only_function f1([](auto i) { return i + 1; }); + assert(f1(1) == 2); + move_only_function f2([](auto i) { return i + 1; }); + assert(f2(2) == 3); + move_only_function f3([](auto i) { return i + 1; }); + assert(std::move(f3)(3) == 4); + + move_only_function f1c([](auto i) { return i + 1; }); + assert(f1c(4) == 5); + move_only_function f2c([](auto i) { return i + 1; }); + assert(f2c(5) == 6); + move_only_function f3c([](auto i) { return i + 1; }); + assert(std::move(f3c)(6) == 7); + + move_only_function f1_nx([](auto i) noexcept { return i + 1; }); + assert(f1_nx(1) == 2); + move_only_function f2_nx([](auto i) noexcept { return i + 1; }); + assert(f2_nx(2) == 3); + move_only_function f3_nx([](auto i) noexcept { return i + 1; }); + assert(std::move(f3_nx)(3) == 4); + + move_only_function f1c_nx([](auto i) noexcept { return i + 1; }); + assert(f1c_nx(4) == 5); + move_only_function f2c_nx([](auto i) noexcept { return i + 1; }); + assert(f2c_nx(5) == 6); + move_only_function f3c_nx([](auto i) noexcept { return i + 1; }); + assert(std::move(f3c_nx)(6) == 7); +} + static_assert(is_same_v::result_type, void>); static_assert(is_same_v::result_type, short>); static_assert(is_same_v::result_type, int>); @@ -471,7 +502,9 @@ int main() { test_swap(); test_ptr(); test_inner(); + test_inplace_list(); test_noexcept(); test_const(); + test_qual(); test_empty(); } From 80f9b0ec3159febe5a79a430013d1e3093728b7c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 22:19:40 +0200 Subject: [PATCH 092/112] Suppress IDE warning --- tests/std/tests/P0288R9_move_only_function/test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 7b2c539c078..91eb5f2740c 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -292,6 +292,7 @@ void test_empty() { 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); From 63975dd8321df9e75f22f0b58f58939dfc7a52f9 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Fri, 19 Nov 2021 22:21:21 +0200 Subject: [PATCH 093/112] Suppress IDE warning --- tests/std/tests/P0288R9_move_only_function/test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 91eb5f2740c..32765479c8c 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -337,6 +337,7 @@ void test_inner() { move_only_function f1(nullptr); move_only_function f2 = move(f1); assert(!f2); +#pragma warning(suppress : 26800) // use a moved-from object f2 = move(f1); assert(!f1); } From 53d260c590590556962e387ee874f4008d79138a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 19 Nov 2021 15:29:23 -0800 Subject: [PATCH 094/112] Comment spelling/grammar. --- stl/inc/functional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 6f5965686da..9a1cf9c25c4 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1243,8 +1243,8 @@ union alignas(max_align_t) _Move_only_function_data { } }; -// Size of large funtion. Treat empty function as if has this size. -// Treat small function as if has this size too if fits and trivially copyable. +// 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*); template From 178998d3bc584c9aa26149e3760e359c8d3eebb0 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 19 Nov 2021 15:38:59 -0800 Subject: [PATCH 095/112] Explicit template args for _Call are now unnecessary. --- stl/inc/functional | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 9a1cf9c25c4..92310947d02 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1255,13 +1255,13 @@ template template _NODISCARD _Rx __stdcall _Function_inv_small(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) { - return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>( + return _Invoker_ret<_Rx>::_Call( static_cast<_VtInvQuals>(*_Self._Small_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...); } template _NODISCARD _Rx __stdcall _Function_inv_large(const _Move_only_function_data& _Self, _Types&&... _Args) noexcept(_Noex) { - return _Invoker_ret<_Rx>::template _Call<_VtInvQuals>( + return _Invoker_ret<_Rx>::_Call( static_cast<_VtInvQuals>(*_Self._Large_fn_ptr<_Vt>()), _STD forward<_Types>(_Args)...); } From c8efdc2f54dc49f83ef85a2c0767c4e0d7d8037e Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 19 Nov 2021 15:57:57 -0800 Subject: [PATCH 096/112] Comment `#endif // 0`. --- stl/inc/functional | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 92310947d02..aa0b6d66e2e 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1591,7 +1591,7 @@ int main() { } } } -#endif +#endif // 0 template class _Move_only_function_call<_Rx(_Types...)> : public _Move_only_function_base<_Rx, false, _Types...> { From b57ed10e248f708bbb22f1612078dfe0424252f6 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 19 Nov 2021 16:35:51 -0800 Subject: [PATCH 097/112] Repair/cleanup generator. * Repair: Change the noexcept machinery from `static constexpr bool _Noexcept` to the latest form. * Use static for the constexpr inst array. * The raw string literal doesn't contain quotes, so we can simply use `R"()"` instead of `R"--()--"`. * De-indent the raw string literal, to match the emitted product code. * Also make ref_str static. * Make ref_str constexpr. Previously, it was an array of modifiable pointers to const char. * clang-format the generator. * Change `noex` to `bool` as it's always treated that way. * Also add `const` to all loop variables. * Use `const auto` instead of `const char* const` for simplicity. * Extract `trait` as it's repeated 3 times. This will also permit line unwrapping. * With these changes, the generator's output matches the PR, modulo clang-formatting and a couple of empty lines. (clang-format's behavior can't be easily replicated in the generator, so I concluded it wasn't worth the effort to attempt to match it more closely by avoiding emitting spaces in certain places.) --- stl/inc/functional | 59 ++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index aa0b6d66e2e..76b82bf72db 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1537,52 +1537,49 @@ class _Move_only_function_call { #include int main() { - constexpr char inst[] = R"--( - template - class _Move_only_function_call<_Rx(_Types...) {0} {1} {3}> : public _Move_only_function_base<_Rx, _Types...> {{ - public: - using result_type = _Rx; - - template - using _VtInvQuals = {0} _Vt {2}; + static constexpr char inst[] = R"( +template +class _Move_only_function_call<_Rx(_Types...) {0} {1} {3}> : public _Move_only_function_base<_Rx, {4}, _Types...> {{ +public: + using result_type = _Rx; - static constexpr bool _Noexcept = {4}; + template + using _VtInvQuals = {0} _Vt {2}; - template - static constexpr bool _Is_callable_from = {5}; + template + static constexpr bool _Is_callable_from = {5}; - _Rx operator()(_Types... _Args) {0} {1} {3} {{ - return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); - }} - }}; -)--"; + _Rx operator()(_Types... _Args) {0} {1} {3} {{ + return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); + }} +}}; +)"; - const char* ref_str[] = { "", "&", "&&" }; + static constexpr const char* ref_str[] = {"", "&", "&&"}; - for (int noex : {0, 1}) { + for (const bool noex : {false, true}) { if (noex) { std::cout << "#ifdef __cpp_noexcept_function_type\n"; } - for (const char* cv : { "", "const" }) { - for (int ref : {0, 1, 2}) { - + for (const auto cv : {"", "const"}) { + for (const int ref : {0, 1, 2}) { + const auto trait = noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v"; std::string callable; - switch (ref) - { - case 0: callable = std::format("{0}<_Rx, {1} _Vt, _Types...> && {0}<_Rx, {1} _Vt&, _Types...>", - noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); + switch (ref) { + case 0: + callable = std::format("{0}<_Rx, {1} _Vt, _Types...> && {0}<_Rx, {1} _Vt&, _Types...>", trait, cv); break; - case 1: callable = std::format("{0}<_Rx, {1} _Vt&, _Types...>", - noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); + case 1: + callable = std::format("{0}<_Rx, {1} _Vt&, _Types...>", trait, cv); break; - case 2: callable = std::format("{0}<_Rx, {1} _Vt, _Types...>", - noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v", cv); + case 2: + callable = std::format("{0}<_Rx, {1} _Vt, _Types...>", trait, cv); break; } - std::cout << std::format(inst, cv, ref_str[ref], ref == 2 ? "&&" : "&", - noex ? "noexcept" : "", noex ? "true" : "false", callable); + std::cout << std::format(inst, cv, ref_str[ref], ref == 2 ? "&&" : "&", noex ? "noexcept" : "", + noex ? "true" : "false", callable); } } From 69abe7ee5b34614c7241b51b2a988a9dcbfdf411 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 19 Nov 2021 17:04:05 -0800 Subject: [PATCH 098/112] Test is already using namespace std. --- tests/std/tests/P0288R9_move_only_function/test.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 32765479c8c..24671dd6938 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -345,7 +345,7 @@ void test_inner() { void test_inplace_list() { struct in_place_list_constructible { - in_place_list_constructible(std::initializer_list li) { + in_place_list_constructible(initializer_list li) { int x = 0; for (int i : li) { ++x; @@ -353,7 +353,7 @@ void test_inplace_list() { } } - in_place_list_constructible(std::initializer_list li, const char*) { + in_place_list_constructible(initializer_list li, const char*) { int x = 0; for (int i : li) { --x; @@ -454,28 +454,28 @@ void test_qual() { move_only_function f2([](auto i) { return i + 1; }); assert(f2(2) == 3); move_only_function f3([](auto i) { return i + 1; }); - assert(std::move(f3)(3) == 4); + assert(move(f3)(3) == 4); move_only_function f1c([](auto i) { return i + 1; }); assert(f1c(4) == 5); move_only_function f2c([](auto i) { return i + 1; }); assert(f2c(5) == 6); move_only_function f3c([](auto i) { return i + 1; }); - assert(std::move(f3c)(6) == 7); + assert(move(f3c)(6) == 7); move_only_function f1_nx([](auto i) noexcept { return i + 1; }); assert(f1_nx(1) == 2); move_only_function f2_nx([](auto i) noexcept { return i + 1; }); assert(f2_nx(2) == 3); move_only_function f3_nx([](auto i) noexcept { return i + 1; }); - assert(std::move(f3_nx)(3) == 4); + assert(move(f3_nx)(3) == 4); move_only_function f1c_nx([](auto i) noexcept { return i + 1; }); assert(f1c_nx(4) == 5); move_only_function f2c_nx([](auto i) noexcept { return i + 1; }); assert(f2c_nx(5) == 6); move_only_function f3c_nx([](auto i) noexcept { return i + 1; }); - assert(std::move(f3c_nx)(6) == 7); + assert(move(f3c_nx)(6) == 7); } static_assert(is_same_v::result_type, void>); From ad01133848848372a87bae223179997d48d55a60 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 19 Nov 2021 17:08:48 -0800 Subject: [PATCH 099/112] Switch parameter types to avoid theoretical narrowing. --- tests/std/tests/P0288R9_move_only_function/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 24671dd6938..db7ae9ff1ab 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -334,8 +334,8 @@ void test_ptr() { } void test_inner() { - move_only_function f1(nullptr); - move_only_function f2 = move(f1); + move_only_function f1(nullptr); + move_only_function f2 = move(f1); assert(!f2); #pragma warning(suppress : 26800) // use a moved-from object f2 = move(f1); From 7b8a26e6da1a3242df4cdc14531c75d43b4d61d6 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 19 Nov 2021 17:13:58 -0800 Subject: [PATCH 100/112] Call test_empty() in definition order. --- tests/std/tests/P0288R9_move_only_function/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index db7ae9ff1ab..66d2c52b1de 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -502,11 +502,11 @@ int main() { test_construct_impl(42, plain_callable); test_assign(); test_swap(); + test_empty(); test_ptr(); test_inner(); test_inplace_list(); test_noexcept(); test_const(); test_qual(); - test_empty(); } From e4fa85115ba0dc3541aa4cb225a88d48219863aa Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 20 Nov 2021 06:54:12 +0200 Subject: [PATCH 101/112] DevCom-1208330 workaround with less repetitions --- stl/inc/functional | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index 6f5965686da..4beeb78ef24 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1343,34 +1343,30 @@ _NODISCARD void* _Function_new_large(_CTypes&&... _Args) { template class _Move_only_function_base { public: + // TRANSITION, DevCom-1208330: use noexcept(_Noexcept) instead template - struct _Impl_t_x { // A per-callable-type structure acting as a virtual function table. + struct _Invoke_t { + using _Call = _Rx(__stdcall*)(const _Move_only_function_data&, _Types&&...); + }; + + template <> + struct _Invoke_t { + 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 - _Rx(__stdcall* _Invoke)(const _Move_only_function_data&, _Types&&...); - // 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; - }; - - // Noexcept specialization - // TRANSITION, DevCom-1208330: use noexcept(_Noexcept) instead - template <> - struct _Impl_t_x { - // Calls target - _Rx(__stdcall* _Invoke)(const _Move_only_function_data&, _Types&&...) _NOEXCEPT_FNPTR; + 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; }; - using _Impl_t = _Impl_t_x<_Noexcept>; - static constexpr _Impl_t _Null_move_only_function = { _Function_not_callable<_Rx, _Types...>, nullptr, From a451335464a5c9612fd95bb2a807934f2000cb25 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 11 Dec 2021 12:59:45 +0200 Subject: [PATCH 102/112] Move out the script and convert it to Python --- stl/inc/functional | 63 +------------------ .../move_only_function_specializations.py | 37 +++++++++++ 2 files changed, 40 insertions(+), 60 deletions(-) create mode 100644 tools/move_only_function_specializations/move_only_function_specializations.py diff --git a/stl/inc/functional b/stl/inc/functional index 30a5d56087d..290aa7ab985 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1525,66 +1525,9 @@ class _Move_only_function_call { "Unlike std::function, std::move_only_function does not define class template argument deduction guides."); }; -// A script to generate the specializations below. -// Avoiding C++ preprocessor for better IDE navigation and debugging experience. -#if 0 -#include -#include -#include - -int main() { - static constexpr char inst[] = R"( -template -class _Move_only_function_call<_Rx(_Types...) {0} {1} {3}> : public _Move_only_function_base<_Rx, {4}, _Types...> {{ -public: - using result_type = _Rx; - - template - using _VtInvQuals = {0} _Vt {2}; - - template - static constexpr bool _Is_callable_from = {5}; - - _Rx operator()(_Types... _Args) {0} {1} {3} {{ - return this->_Get_invoke()(this->_Data, _STD forward<_Types>(_Args)...); - }} -}}; -)"; - - static constexpr const char* ref_str[] = {"", "&", "&&"}; - - for (const bool noex : {false, true}) { - if (noex) { - std::cout << "#ifdef __cpp_noexcept_function_type\n"; - } - - for (const auto cv : {"", "const"}) { - for (const int ref : {0, 1, 2}) { - const auto trait = noex ? "is_nothrow_invocable_r_v" : "is_invocable_r_v"; - std::string callable; - switch (ref) { - case 0: - callable = std::format("{0}<_Rx, {1} _Vt, _Types...> && {0}<_Rx, {1} _Vt&, _Types...>", trait, cv); - break; - case 1: - callable = std::format("{0}<_Rx, {1} _Vt&, _Types...>", trait, cv); - break; - case 2: - callable = std::format("{0}<_Rx, {1} _Vt, _Types...>", trait, cv); - break; - } - - std::cout << std::format(inst, cv, ref_str[ref], ref == 2 ? "&&" : "&", noex ? "noexcept" : "", - noex ? "true" : "false", callable); - } - } - - if (noex) { - std::cout << "#endif // __cpp_noexcept_function_type\n"; - } - } -} -#endif // 0 +// 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 _Move_only_function_call<_Rx(_Types...)> : public _Move_only_function_base<_Rx, false, _Types...> { diff --git a/tools/move_only_function_specializations/move_only_function_specializations.py b/tools/move_only_function_specializations/move_only_function_specializations.py new file mode 100644 index 00000000000..0afb9185170 --- /dev/null +++ b/tools/move_only_function_specializations/move_only_function_specializations.py @@ -0,0 +1,37 @@ +template = """template +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 + using _VtInvQuals = {cv} _Vt {ref_inv}; + + template + 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") From 925de8bdd0adc5a6380168420feb9347604ccc91 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 11 Dec 2021 13:25:14 +0200 Subject: [PATCH 103/112] implicit conversion to pointer to function --- tests/std/tests/P0288R9_move_only_function/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 66d2c52b1de..e3438ee0850 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -104,7 +104,7 @@ struct large_implicit_ptr_callable : counter { using pfn = int (*)(int a, pass_this_by_ref& b); operator pfn() { - return +[](int a, pass_this_by_ref& b) { + return [](int a, pass_this_by_ref& b) { assert(a == 23); assert(b.v == 63); return 41; From 01dfb5b16b5b321de3c5279653331c7e9a683a20 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 11 Dec 2021 13:43:58 +0200 Subject: [PATCH 104/112] document --- stl/inc/functional | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 290aa7ab985..b1bcc9cb875 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1208,6 +1208,10 @@ _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept { #endif // !_HAS_CXX20 #if _HAS_CXX23 +// _Move_only_function_data is defined as array of pointers. +// The first element is always pointer to _Move_only_function_base::_Impl_t, it emulates vtable pointer. +// The other pointers are used as storage for small functor; +// if the functor does not fit in, the second pointer is the pointer to allocated sotrage, the rest are unused. union alignas(max_align_t) _Move_only_function_data { void* _Pointers[_Small_object_num_ptrs]; const void* _Impl; @@ -1247,6 +1251,10 @@ union alignas(max_align_t) _Move_only_function_data { // 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) +// Still non-template functions are defined inline, as the compiler may be able to devirtualize some calls. + template [[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 @@ -1526,7 +1534,7 @@ class _Move_only_function_call { }; // A script to generate the specializations is at -// /tools/\move_only_function_specializations/move_only_function_specializations.py +// /tools/move_only_function_specializations/move_only_function_specializations.py // (Avoiding C++ preprocessor for better IDE navigation and debugging experience) template From 49099a76088a126378c3d0d77feaccb0f9b6a571 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Sat, 11 Dec 2021 13:44:31 +0200 Subject: [PATCH 105/112] -whitespace --- .../move_only_function_specializations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/move_only_function_specializations/move_only_function_specializations.py b/tools/move_only_function_specializations/move_only_function_specializations.py index 0afb9185170..0e57fb70cdd 100644 --- a/tools/move_only_function_specializations/move_only_function_specializations.py +++ b/tools/move_only_function_specializations/move_only_function_specializations.py @@ -1,5 +1,5 @@ template = """template -class _Move_only_function_call<_Rx(_Types...) {cv} {ref} {noex}> +class _Move_only_function_call<_Rx(_Types...) {cv} {ref} {noex}> : public _Move_only_function_base<_Rx, {noex_val}, _Types...> {{ public: using result_type = _Rx; From bde333a6b4bafcf7134e5923d0568313a162f56e Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 13 Dec 2021 19:49:03 -0800 Subject: [PATCH 106/112] Fix comment typo/grammar. --- stl/inc/functional | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index b1bcc9cb875..07ed1ed1265 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1208,10 +1208,10 @@ _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept { #endif // !_HAS_CXX20 #if _HAS_CXX23 -// _Move_only_function_data is defined as array of pointers. -// The first element is always pointer to _Move_only_function_base::_Impl_t, it emulates vtable pointer. -// The other pointers are used as storage for small functor; -// if the functor does not fit in, the second pointer is the pointer to allocated sotrage, the rest are unused. +// _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; @@ -1252,8 +1252,8 @@ union alignas(max_align_t) _Move_only_function_data { 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) -// Still non-template functions are defined inline, as the compiler may be able to devirtualize some calls. +// (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 [[noreturn]] _Rx __stdcall _Function_not_callable(const _Move_only_function_data&, _Types&&...) noexcept { From b48185c9cbcb0f748094edc3a71355303acc955e Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 13 Dec 2021 19:49:48 -0800 Subject: [PATCH 107/112] Add banner, remove unnecessary semicolon. --- .../move_only_function_specializations.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/move_only_function_specializations/move_only_function_specializations.py b/tools/move_only_function_specializations/move_only_function_specializations.py index 0e57fb70cdd..1d17973bb09 100644 --- a/tools/move_only_function_specializations/move_only_function_specializations.py +++ b/tools/move_only_function_specializations/move_only_function_specializations.py @@ -1,3 +1,6 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + template = """template class _Move_only_function_call<_Rx(_Types...) {cv} {ref} {noex}> : public _Move_only_function_base<_Rx, {noex_val}, _Types...> {{ @@ -13,7 +16,7 @@ class _Move_only_function_call<_Rx(_Types...) {cv} {ref} {noex}> _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) From 7a72a01e79f5ff989f8796592e97b9991b0ba5aa Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 14 Dec 2021 23:49:55 +0200 Subject: [PATCH 108/112] fix uninitialized guard pointer make sure no uninitialized pointer deletion happens if new throws --- stl/inc/functional | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index b1bcc9cb875..1d6f3a07aae 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1338,14 +1338,16 @@ _NODISCARD void* _Function_new_large(_CTypes&&... _Args) { } }; - _Guard_type _Guard; + void* _Ptr; if constexpr (alignof(_Vt) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { - _Guard._Ptr = ::operator new(sizeof(_Vt)); + _Ptr = ::operator new(sizeof(_Vt)); } else { - _Guard._Ptr = ::operator new (sizeof(_Vt), align_val_t{alignof(_Vt)}); + _Ptr = ::operator new (sizeof(_Vt), align_val_t{alignof(_Vt)}); } - ::new (_Guard._Ptr) _Vt(_STD forward<_CTypes>(_Args)...); - return _STD exchange(_Guard._Ptr, nullptr); + _Guard_type _Guard{_Ptr}; + ::new (_Ptr) _Vt(_STD forward<_CTypes>(_Args)...); + _Guard._Ptr = nullptr; + return _Ptr; } template From a9db0b890050bbc67c5d0d74bfdc434615b113ff Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 15 Dec 2021 00:29:13 +0200 Subject: [PATCH 109/112] also bug fix on throwing constructor --- stl/inc/functional | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index e9de07e9779..317d4bbb76c 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1411,10 +1411,6 @@ public: } } - ~_Move_only_function_base() { - _Checked_destroy(_Data); - } - static void _Checked_destroy(_Move_only_function_data& _Data) noexcept { const auto _Impl = static_cast(_Data._Impl); if (_Impl->_Destroy) { @@ -1809,6 +1805,12 @@ public: 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(); From 4fd0c31160fa1bfe19b5877ac1ce0bea6e76ced2 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 15 Dec 2021 00:35:28 +0200 Subject: [PATCH 110/112] +coverage --- .../tests/P0288R9_move_only_function/test.cpp | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index e3438ee0850..156e794d34c 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -4,8 +4,10 @@ #include #include #include +#include #include #include +#include using namespace std; @@ -494,6 +496,57 @@ static_assert(is_same_v::result static_assert(is_same_v::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(std::size_t size) { + if (fail_allocations) { + throw bad_alloc{}; + } + void* result = size ? malloc(size) : malloc(1); + if (!result) { + throw bad_alloc{}; + } + return result; +} + +void operator delete(void* p) noexcept { + if (p) { + 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 f{throwing{}}; + assert(false); // unreachable + } catch (runtime_error&) { + } + + + try { + fail_allocations = true; + move_only_function f{not_throwing{}}; + assert(false); // unreachable + } catch (bad_alloc&) { + fail_allocations = false; + } +} + int main() { test_construct_impl(38); test_construct_impl(39); @@ -509,4 +562,5 @@ int main() { test_noexcept(); test_const(); test_qual(); + test_except(); } From 949e038e2c72204b841b3627e0d48a7595606f8f Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 15 Dec 2021 00:38:20 +0200 Subject: [PATCH 111/112] -std --- tests/std/tests/P0288R9_move_only_function/test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 156e794d34c..78d3faafa3e 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -499,7 +499,7 @@ static_assert(is_same_v::result_ bool fail_allocations = false; #pragma warning(suppress : 28251) // Inconsistent annotation for 'new': this instance has no annotations. -void* operator new(std::size_t size) { +void* operator new(size_t size) { if (fail_allocations) { throw bad_alloc{}; } @@ -537,7 +537,6 @@ void test_except() { } catch (runtime_error&) { } - try { fail_allocations = true; move_only_function f{not_throwing{}}; From 259cf68df9f198dd2ec70f5c170c95c21a71b649 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 14 Dec 2021 18:47:20 -0800 Subject: [PATCH 112/112] Minor test style changes. --- tests/std/tests/P0288R9_move_only_function/test.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P0288R9_move_only_function/test.cpp b/tests/std/tests/P0288R9_move_only_function/test.cpp index 78d3faafa3e..4b56b5076d7 100644 --- a/tests/std/tests/P0288R9_move_only_function/test.cpp +++ b/tests/std/tests/P0288R9_move_only_function/test.cpp @@ -4,10 +4,10 @@ #include #include #include +#include #include #include #include -#include using namespace std; @@ -503,7 +503,7 @@ void* operator new(size_t size) { if (fail_allocations) { throw bad_alloc{}; } - void* result = size ? malloc(size) : malloc(1); + void* result = size > 0 ? malloc(size) : malloc(1); if (!result) { throw bad_alloc{}; } @@ -511,9 +511,7 @@ void* operator new(size_t size) { } void operator delete(void* p) noexcept { - if (p) { - free(p); - } + free(p); } void test_except() {