-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
base: main
Are you sure you want to change the base?
Changes from 3 commits
5195dd0
a57cf70
edc3b57
5e3ea4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -466,16 +466,29 @@ template <class _Ret, class... _ArgTypes> | |
class _Packaged_state<_Ret(_ArgTypes...)> | ||
: public _Associated_state<_Ret> { // class for managing associated asynchronous state for packaged_task | ||
public: | ||
using _Mybase = _Associated_state<_Ret>; | ||
using _Mydel = typename _Mybase::_Mydel; | ||
using _Mybase = _Associated_state<_Ret>; | ||
using _Mydel = typename _Mybase::_Mydel; | ||
using _Function_type = function<_Ret(_ArgTypes...)>; | ||
|
||
template <class _Fty2> | ||
_Packaged_state(_Fty2&& _Fnarg) : _Fn(_STD forward<_Fty2>(_Fnarg)) {} | ||
explicit _Packaged_state(const _Function_type& _Fnarg) : _Fn(_Fnarg) {} | ||
|
||
explicit _Packaged_state(_Function_type&& _Fnarg) noexcept : _Fn(_STD move(_Fnarg)) {} | ||
|
||
template <class _Fty2, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, _Function_type>, int> = 0> | ||
explicit _Packaged_state(_Fty2&& _Fnarg) : _Fn(_Secret_copyability_ignoring_tag{}, _STD forward<_Fty2>(_Fnarg)) {} | ||
|
||
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT | ||
template <class _Fty2, class _Alloc> | ||
template <class _Alloc> | ||
_Packaged_state(const _Function_type& _Fnarg, const _Alloc& _Al, _Mydel* _Dp) | ||
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _Fnarg) {} | ||
|
||
template <class _Alloc> | ||
_Packaged_state(_Function_type&& _Fnarg, const _Alloc& _Al, _Mydel* _Dp) | ||
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _STD move(_Fnarg)) {} | ||
|
||
template <class _Fty2, class _Alloc, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, _Function_type>, int> = 0> | ||
_Packaged_state(_Fty2&& _Fnarg, const _Alloc& _Al, _Mydel* _Dp) | ||
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _STD forward<_Fty2>(_Fnarg)) {} | ||
: _Mybase(_Dp), _Fn(_Secret_copyability_ignoring_tag{}, allocator_arg, _Al, _STD forward<_Fty2>(_Fnarg)) {} | ||
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT | ||
|
||
void _Call_deferred(_ArgTypes... _Args) { // set deferred call | ||
|
@@ -498,28 +511,44 @@ public: | |
_CATCH_END | ||
} | ||
|
||
const auto& _Get_fn() const { | ||
const auto& _Get_fn() const& { | ||
return _Fn; | ||
} | ||
auto&& _Get_fn() && noexcept { | ||
return _STD move(_Fn); | ||
} | ||
|
||
private: | ||
function<_Ret(_ArgTypes...)> _Fn; | ||
_Function_type _Fn; | ||
}; | ||
|
||
template <class _Ret, class... _ArgTypes> | ||
class _Packaged_state<_Ret&(_ArgTypes...)> | ||
: public _Associated_state<_Ret*> { // class for managing associated asynchronous state for packaged_task | ||
public: | ||
using _Mybase = _Associated_state<_Ret*>; | ||
using _Mydel = typename _Mybase::_Mydel; | ||
using _Mybase = _Associated_state<_Ret*>; | ||
using _Mydel = typename _Mybase::_Mydel; | ||
using _Function_type = function<_Ret&(_ArgTypes...)>; | ||
|
||
template <class _Fty2> | ||
_Packaged_state(_Fty2&& _Fnarg) : _Fn(_STD forward<_Fty2>(_Fnarg)) {} | ||
explicit _Packaged_state(const _Function_type& _Fnarg) : _Fn(_Fnarg) {} | ||
|
||
explicit _Packaged_state(_Function_type&& _Fnarg) noexcept : _Fn(_STD move(_Fnarg)) {} | ||
|
||
template <class _Fty2, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, _Function_type>, int> = 0> | ||
explicit _Packaged_state(_Fty2&& _Fnarg) : _Fn(_Secret_copyability_ignoring_tag{}, _STD forward<_Fty2>(_Fnarg)) {} | ||
|
||
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT | ||
template <class _Fty2, class _Alloc> | ||
template <class _Alloc> | ||
_Packaged_state(const _Function_type& _Fnarg, const _Alloc& _Al, _Mydel* _Dp) | ||
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _Fnarg) {} | ||
|
||
template <class _Alloc> | ||
_Packaged_state(_Function_type&& _Fnarg, const _Alloc& _Al, _Mydel* _Dp) | ||
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _STD move(_Fnarg)) {} | ||
|
||
template <class _Fty2, class _Alloc, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, _Function_type>, int> = 0> | ||
_Packaged_state(_Fty2&& _Fnarg, const _Alloc& _Al, _Mydel* _Dp) | ||
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _STD forward<_Fty2>(_Fnarg)) {} | ||
: _Mybase(_Dp), _Fn(_Secret_copyability_ignoring_tag{}, allocator_arg, _Al, _STD forward<_Fty2>(_Fnarg)) {} | ||
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT | ||
|
||
void _Call_deferred(_ArgTypes... _Args) { // set deferred call | ||
|
@@ -542,28 +571,44 @@ public: | |
_CATCH_END | ||
} | ||
|
||
const auto& _Get_fn() const { | ||
const auto& _Get_fn() const& { | ||
return _Fn; | ||
} | ||
auto&& _Get_fn() && noexcept { | ||
return _STD move(_Fn); | ||
} | ||
|
||
private: | ||
function<_Ret&(_ArgTypes...)> _Fn; | ||
_Function_type _Fn; | ||
}; | ||
|
||
template <class... _ArgTypes> | ||
class _Packaged_state<void(_ArgTypes...)> | ||
: public _Associated_state<int> { // class for managing associated asynchronous state for packaged_task | ||
public: | ||
using _Mybase = _Associated_state<int>; | ||
using _Mydel = typename _Mybase::_Mydel; | ||
using _Mybase = _Associated_state<int>; | ||
using _Mydel = typename _Mybase::_Mydel; | ||
using _Function_type = function<void(_ArgTypes...)>; | ||
|
||
template <class _Fty2> | ||
_Packaged_state(_Fty2&& _Fnarg) : _Fn(_STD forward<_Fty2>(_Fnarg)) {} | ||
explicit _Packaged_state(const _Function_type& _Fnarg) : _Fn(_Fnarg) {} | ||
|
||
explicit _Packaged_state(_Function_type&& _Fnarg) noexcept : _Fn(_STD move(_Fnarg)) {} | ||
|
||
template <class _Fty2, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, _Function_type>, int> = 0> | ||
explicit _Packaged_state(_Fty2&& _Fnarg) : _Fn(_Secret_copyability_ignoring_tag{}, _STD forward<_Fty2>(_Fnarg)) {} | ||
|
||
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT | ||
template <class _Fty2, class _Alloc> | ||
template <class _Alloc> | ||
_Packaged_state(const _Function_type& _Fnarg, const _Alloc& _Al, _Mydel* _Dp) | ||
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _Fnarg) {} | ||
|
||
template <class _Alloc> | ||
_Packaged_state(_Function_type&& _Fnarg, const _Alloc& _Al, _Mydel* _Dp) | ||
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _STD move(_Fnarg)) {} | ||
|
||
template <class _Fty2, class _Alloc, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, _Function_type>, int> = 0> | ||
_Packaged_state(_Fty2&& _Fnarg, const _Alloc& _Al, _Mydel* _Dp) | ||
: _Mybase(_Dp), _Fn(allocator_arg, _Al, _STD forward<_Fty2>(_Fnarg)) {} | ||
: _Mybase(_Dp), _Fn(_Secret_copyability_ignoring_tag{}, allocator_arg, _Al, _STD forward<_Fty2>(_Fnarg)) {} | ||
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT | ||
|
||
void _Call_deferred(_ArgTypes... _Args) { // set deferred call | ||
|
@@ -588,12 +633,15 @@ public: | |
_CATCH_END | ||
} | ||
|
||
const auto& _Get_fn() const { | ||
const auto& _Get_fn() const& { | ||
return _Fn; | ||
} | ||
auto&& _Get_fn() && noexcept { | ||
return _STD move(_Fn); | ||
} | ||
|
||
private: | ||
function<void(_ArgTypes...)> _Fn; | ||
_Function_type _Fn; | ||
}; | ||
|
||
template <class _Ty, class _Alloc> | ||
|
@@ -1266,7 +1314,10 @@ public: | |
packaged_task() = default; | ||
|
||
template <class _Fty2, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, packaged_task>, int> = 0> | ||
explicit packaged_task(_Fty2&& _Fnarg) : _MyPromise(new _MyStateType(_STD forward<_Fty2>(_Fnarg))) {} | ||
explicit packaged_task(_Fty2&& _Fnarg) : _MyPromise(new _MyStateType(_STD forward<_Fty2>(_Fnarg))) { | ||
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)."); | ||
Comment on lines
+1318
to
+1319
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
|
||
packaged_task(packaged_task&&) noexcept = default; | ||
|
||
|
@@ -1275,7 +1326,10 @@ public: | |
#if _HAS_FUNCTION_ALLOCATOR_SUPPORT | ||
template <class _Fty2, class _Alloc, enable_if_t<!is_same_v<_Remove_cvref_t<_Fty2>, packaged_task>, int> = 0> | ||
packaged_task(allocator_arg_t, const _Alloc& _Al, _Fty2&& _Fnarg) | ||
: _MyPromise(_STD _Make_packaged_state<_MyStateType>(_STD forward<_Fty2>(_Fnarg), _Al)) {} | ||
: _MyPromise(_STD _Make_packaged_state<_MyStateType>(_STD forward<_Fty2>(_Fnarg), _Al)) { | ||
static_assert(_Is_invocable_r<_Ret, _Fty2&, _ArgTypes...>::value, | ||
"The function object must be callable with _ArgTypes... and return _Ret (N4140 [futures.task.members]/2)."); | ||
} | ||
#endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT | ||
|
||
~packaged_task() noexcept { | ||
|
@@ -1319,9 +1373,9 @@ public: | |
} | ||
|
||
void reset() { // reset to newly constructed state | ||
_MyStateManagerType& _State = _MyPromise._Get_state_for_set(); | ||
_MyStateType* _MyState = static_cast<_MyStateType*>(_State._Ptr()); | ||
_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())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's perhaps necessary to There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
_MyPromise._Get_state()._Abandon(); | ||
_MyPromise._Swap(_New_promise); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -122,7 +122,6 @@ void test_function() { | |
|
||
void test_packaged_task() { | ||
packaged_task<void(validator)>{}; | ||
packaged_task<void(validator)>{nullptr}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
packaged_task<void(validator)>{simple_identity{}}; | ||
packaged_task<void(validator)>{simple_large_identity{}}; | ||
|
||
|
There was a problem hiding this comment.
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
?There was a problem hiding this comment.
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.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
_SecretTag
constraint should probably be first so we avoid evaluating the other constraint when it's not satisfied.