[libcxx-commits] [PATCH] D130631: [libc++] Implement P0288R9 (move_only_function)
Zhihao Yuan via Phabricator via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Aug 10 12:17:23 PDT 2022
lichray requested changes to this revision.
lichray added inline comments.
================
Comment at: libcxx/include/__functional/move_only_function.h:26
+# define _LIBCPP_MOVE_ONLY_FUNCTION_REF &
+# include <__functional/move_only_function_impl.h>
+
----------------
We should aim for a macro-free implementation to benefit users.
================
Comment at: libcxx/include/__functional/move_only_function_impl.h:70
+ using _DestroyFn = void(void*);
+ using _CallFn = void;
+
----------------
In his CppCon 2017 talk, Louis @ldionne showed that embedding a vtable in the object storage is not an optimization compared to using a vptr even if we ignore the fact that embedded vtable uses more space. Also, mo-func of 6 x pointers in size may be too large. It's even larger than Swift's existential container (3-pointers + metadata + vptr).
================
Comment at: libcxx/include/__functional/move_only_function_impl.h:203
+ _LIBCPP_HIDE_FROM_ABI static _ReturnT
+ __call(void* __functor, _ArgTypes&&... __args) noexcept(_LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT) {
+ return std::invoke(static_cast<_Functor _LIBCPP_MOVE_ONLY_FUNCTION_INV_QUALS>(*static_cast<_Functor*>(__functor)),
----------------
avogelsgesang wrote:
> avogelsgesang wrote:
> > `__functor` should probably be the last argument.
> >
> > This function forwards all its arguments except for `__functor`.
> > Removing an argument from the front of the argument list means that all parameters must be "shifted" between the registers/on the stack.
> > If instead `functor` is the last argument, no arguments need to be moved around.
> >
> > See https://godbolt.org/z/hnbe1xYdf
> >
> > This is also beneficial for `operator()`: `operator()` has to add the `functor` to the argument list. Appending an argument is trivial, but prepending is expensive...
> nevermind - on 2nd thought: this code is only removing the first argument if `__function` is a function pointer. If it is, e.g., a lambda then the first argument, i.e. the `this` pointer, will be replaced but all other arguments stay in their original position.
>
> Similarly, `operator()` does not actually prepend an argument, but just replaces the first argument, i.e. the `this` pointer
> Similarly, `operator()` does not actually prepend an argument, but just replaces the first argument, i.e. the `this` pointer
Exactly.
================
Comment at: libcxx/include/__functional/move_only_function_impl.h:203
+ _LIBCPP_HIDE_FROM_ABI static _ReturnT
+ __call(void* __functor, _ArgTypes&&... __args) noexcept(_LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT) {
+ return std::invoke(static_cast<_Functor _LIBCPP_MOVE_ONLY_FUNCTION_INV_QUALS>(*static_cast<_Functor*>(__functor)),
----------------
lichray wrote:
> avogelsgesang wrote:
> > avogelsgesang wrote:
> > > `__functor` should probably be the last argument.
> > >
> > > This function forwards all its arguments except for `__functor`.
> > > Removing an argument from the front of the argument list means that all parameters must be "shifted" between the registers/on the stack.
> > > If instead `functor` is the last argument, no arguments need to be moved around.
> > >
> > > See https://godbolt.org/z/hnbe1xYdf
> > >
> > > This is also beneficial for `operator()`: `operator()` has to add the `functor` to the argument list. Appending an argument is trivial, but prepending is expensive...
> > nevermind - on 2nd thought: this code is only removing the first argument if `__function` is a function pointer. If it is, e.g., a lambda then the first argument, i.e. the `this` pointer, will be replaced but all other arguments stay in their original position.
> >
> > Similarly, `operator()` does not actually prepend an argument, but just replaces the first argument, i.e. the `this` pointer
> > Similarly, `operator()` does not actually prepend an argument, but just replaces the first argument, i.e. the `this` pointer
>
> Exactly.
1. `_ArgTypes&&` - should consider passing objects by copy rather than references as an optimization in certain cases.
2. Please make sure that we don't have a design that works like the following: when `move_only_function` is initialized from a pointer to function, we need to dereference `__functor` first to reach the function pointer, and then make a call via that function pointer.
================
Comment at: libcxx/include/__functional/move_only_function_impl.h:267
+ __storage_.__large_.__destroy_(__storage_.__large_.__heap_);
+ ::operator delete(__storage_.__large_.__heap_);
+ break;
----------------
It seems that some logic is embedded in the mo-func body, rather than dispatched. `__destroy_` is not sufficiently used.
================
Comment at: libcxx/include/__functional/move_only_function_impl.h:359
+ noexcept(_LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT) {
+ auto __call = [&](auto&& __callable, auto&& __data) {
+ return std::bit_cast<void (*)(_LIBCPP_MOVE_ONLY_FUNCTION_CV void*, _ArgTypes...)>(__callable)(
----------------
Please make sure the type is ABI-compatible after adopting https://wg21.link/p2511/github. It is on the way to C++26.
================
Comment at: libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/normal_const.pass.cpp:83
+ assert(f);
+ LIBCPP_ASSERT(f.__get_status() == std::__move_only_function_storage::_Status::_TriviallyDestructible);
+ }
----------------
We should test against behaviors, not guts. If the difference is not observable according to the standard, consider observing some extension such as a more strict `noexcept`.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D130631/new/
https://reviews.llvm.org/D130631
More information about the libcxx-commits
mailing list