Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<future>: Make packaged_task accept move-only functors #4946

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

frederick-vs-ja
Copy link
Contributor

@frederick-vs-ja frederick-vs-ja commented Sep 10, 2024

Fixes #321.

The issue was considered ABI-breaking, but I it can be resolved in an ABI-preserving way like #2568. This PR adds internal constructors to function to accept non-copy-constructible functors and add static_assert to keep the copyability checking for standard constructors. Valid user codes won't be able to call these internal constructors.

Also implements the previously missing Mandates in [futures.task.members]/3, and switches to use move construction in reset per [futures.task.members]/26.

Notes:

  • The internal constructors are made constrained templates to avoid affecting overload resolution unexpectedly, see LLVM-103409.
  • The involved allocator-extended constructors were removed in C++17 by WG21-P0302R1 and LWG-2921, so this PR cites WG21-N4140 in the C++17-removed constructors.
  • The fix should work for most move-only types. Although there can be classes whose copy constructor are only invalid in instantation. In vNext we should completely get rid of function.
  • If the function<R(Args...)> is a program-defined specialization, this approach doesn't work. I don't think any user should specialize std::function, but this is allowed by the standard.

@frederick-vs-ja frederick-vs-ja requested a review from a team as a code owner September 10, 2024 05:48
@AlexGuteniev
Copy link
Contributor

  • In vNext we should completely get rid of function.

I think it worth creating vNext issue.

move_only_function might be helpful for vNext implementation.

_MyPromiseType _New_promise(new _MyStateType(_MyState->_Get_fn()));
_MyStateManagerType& _State_mgr = _MyPromise._Get_state_for_set();
_MyStateType& _MyState = *static_cast<_MyStateType*>(_State_mgr._Ptr());
_MyPromiseType _New_promise(new _MyStateType(_STD move(_MyState)._Get_fn()));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's perhaps necessary to move per [futures.task.members]/26. However, the behavior of formerly valid code will change. Not sure whether this is acceptable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems possible to keep old behavior for existing types while enable correct behavior for newly allowed move-only functors - make the new branch of _Copy do const_cast and then call _Move. But that's somehow weird.

@@ -122,7 +122,6 @@ void test_function() {

void test_packaged_task() {
packaged_task<void(validator)>{};
packaged_task<void(validator)>{nullptr};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line was wrong per [futures.task.members]/3 but I didn't realize that when it was added.

@AlexGuteniev
Copy link
Contributor

I think it worth creating vNext issue.

Or maybe TRANSITION, ABI comment(s)

@StephanTLavavej StephanTLavavej added the bug Something isn't working label Sep 10, 2024
@StephanTLavavej StephanTLavavej self-assigned this Sep 10, 2024
Comment on lines +1318 to +1319
static_assert(_Is_invocable_r<_Ret, _Fty2&, _ArgTypes...>::value,
"The function object must be callable with _ArgTypes... and return _Ret (N4988 [futures.task.members]/3).");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the change is exactly reflecting [futures.task.members]/3, but the Mandates doesn't seem very correct when remove_reference_t<_Fty2> is cv-qualified.
Should we use decay_t<_Fty2>& or _Remove_cvref_t<_Fty2>&? I've mailed to LWG Chair to submit an LWG issue to say decay_t in [futures.task.members]/3.

@CaseyCarter

This comment was marked as off-topic.

Comment on lines +1103 to +1105
template <class _SecretTag, class _Fx, typename _Mybase::template _Enable_if_callable_t<_Fx, function> = 0,
enable_if_t<is_same_v<_SecretTag, _Secret_copyability_ignoring_tag>, int> = 0>
explicit function(_SecretTag, _Fx&& _Func) { // used exclusively for packaged_task
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is _SecretTag a template parameter? Could it be just a parameter of type _Secret_copyability_ignoring_tag ?

Copy link
Contributor Author

@frederick-vs-ja frederick-vs-ja Sep 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is necessary for defending against some convertible-to-almost-all types. But perhaps we don't need it as of C++17 since the allocator_arg_t ctors are removed... although users can restore them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: Initial Review
Development

Successfully merging this pull request may close these issues.

<future>: packaged_task can't be constructed from a move-only lambda
4 participants