Skip to content

Commit 1458877

Browse files
committed
Fixing .then to work with movable-only types
1 parent c82795c commit 1458877

File tree

2 files changed

+76
-21
lines changed

2 files changed

+76
-21
lines changed

Release/include/pplx/pplxtasks.h

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3093,6 +3093,44 @@ namespace details
30933093
template<typename _Ty>
30943094
static std::false_type _IsValidCreateAsync(_Ty _Param, ...);
30953095
#endif /* defined (__cplusplus_winrt) */
3096+
3097+
/// <summary>
3098+
/// A helper class template that makes only movable functions be able to be passed to std::function
3099+
/// </summary>
3100+
template<typename _Ty>
3101+
struct _NonCopyableFunctorWrapper
3102+
{
3103+
_NonCopyableFunctorWrapper(_Ty&& f)
3104+
: _M_functor{std::make_shared<_Ty>(std::forward<_Ty>(f))}
3105+
{}
3106+
3107+
template <class... _Args>
3108+
auto operator()(_Args&&... args) -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...))
3109+
{
3110+
return _M_functor->operator()(std::forward<_Args>(args)...);
3111+
}
3112+
3113+
template <class... _Args>
3114+
auto operator()(_Args&&... args) const -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...))
3115+
{
3116+
return _M_functor->operator()(std::forward<_Args>(args)...);
3117+
}
3118+
3119+
std::shared_ptr<_Ty> _M_functor;
3120+
};
3121+
3122+
template<typename _Ty, typename Enable = void>
3123+
struct _CopyableFunctor
3124+
{
3125+
typedef _Ty _Type;
3126+
};
3127+
3128+
template<typename _Ty>
3129+
struct _CopyableFunctor<_Ty, typename std::enable_if<
3130+
std::is_move_constructible<_Ty>::value && !std::is_copy_constructible<_Ty>::value>::type>
3131+
{
3132+
typedef _NonCopyableFunctorWrapper<_Ty> _Type;
3133+
};
30963134
}
30973135
/// <summary>
30983136
/// A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a lambda that takes and returns a
@@ -3160,6 +3198,7 @@ class _Init_func_transformer<void>
31603198
}
31613199
};
31623200

3201+
31633202
/// <summary>
31643203
/// The Parallel Patterns Library (PPL) <c>task</c> class. A <c>task</c> object represents work that can be executed asynchronously,
31653204
/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces
@@ -3424,11 +3463,11 @@ class task
34243463
/**/
34253464
template<typename _Function>
34263465
__declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result
3427-
auto then(const _Function& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType
3466+
auto then(_Function&& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType
34283467
{
34293468
task_options _TaskOptions;
34303469
details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK());
3431-
return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions);
3470+
return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions);
34323471
}
34333472

34343473
/// <summary>
@@ -3457,10 +3496,10 @@ class task
34573496
/**/
34583497
template<typename _Function>
34593498
__declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result
3460-
auto then(const _Function& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType
3499+
auto then(_Function&& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType
34613500
{
34623501
details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK());
3463-
return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions);
3502+
return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions);
34643503
}
34653504

34663505
/// <summary>
@@ -3493,11 +3532,11 @@ class task
34933532
/**/
34943533
template<typename _Function>
34953534
__declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result
3496-
auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType
3535+
auto then(_Function&& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType
34973536
{
34983537
task_options _TaskOptions(_CancellationToken, _ContinuationContext);
34993538
details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK());
3500-
return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions);
3539+
return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions);
35013540
}
35023541

35033542
/// <summary>
@@ -3682,13 +3721,13 @@ class task
36823721
/// This function is Used for runtime internal continuations only.
36833722
/// </summary>
36843723
template<typename _Function>
3685-
auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState,
3724+
auto _Then(_Function&& _Func, details::_CancellationTokenState *_PTokenState,
36863725
details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType
36873726
{
36883727
// inherit from antecedent
36893728
auto _Scheduler = _GetImpl()->_GetScheduler();
36903729

3691-
return _ThenImpl<_ReturnType, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode);
3730+
return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode);
36923731
}
36933732

36943733
private:
@@ -3801,16 +3840,17 @@ class task
38013840
typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type _NormalizedContinuationReturnType;
38023841

38033842
typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl;
3804-
_Function _M_function;
3843+
typename details::_CopyableFunctor<_Function>::_Type _M_function;
38053844

3845+
template <class _ForwardedFunction>
38063846
_ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl,
38073847
const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl,
3808-
const _Function & _Func, const task_continuation_context & _Context, details::_TaskInliningMode_t _InliningMode)
3848+
_ForwardedFunction&& _Func, const task_continuation_context & _Context, details::_TaskInliningMode_t _InliningMode)
38093849
: details::_PPLTaskHandle<typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type,
38103850
_ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase>
38113851
::_PPLTaskHandle(_ContinuationImpl)
38123852
, _M_ancestorTaskImpl(_AncestorImpl)
3813-
, _M_function(_Func)
3853+
, _M_function(std::forward<_ForwardedFunction>(_Func))
38143854
{
38153855
this->_M_isTaskBasedContinuation = _IsTaskBased::value;
38163856
this->_M_continuationContext = _Context;
@@ -4095,7 +4135,7 @@ class task
40954135
}
40964136

40974137
template<typename _InternalReturnType, typename _Function>
4098-
auto _ThenImpl(const _Function& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType
4138+
auto _ThenImpl(_Function&& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType
40994139
{
41004140
if (!_M_Impl)
41014141
{
@@ -4105,14 +4145,14 @@ class task
41054145
details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;
41064146
auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler();
41074147
auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : details::_TaskCreationCallstack();
4108-
return _ThenImpl<_InternalReturnType, _Function>(_Func, _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack);
4148+
return _ThenImpl<_InternalReturnType, _Function>(std::forward<_Function>(_Func), _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack);
41094149
}
41104150

41114151
/// <summary>
41124152
/// The one and only implementation of then for void and non-void tasks.
41134153
/// </summary>
41144154
template<typename _InternalReturnType, typename _Function>
4115-
auto _ThenImpl(const _Function& _Func, details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack,
4155+
auto _ThenImpl(_Function&& _Func, details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack,
41164156
details::_TaskInliningMode_t _InliningMode = details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType
41174157
{
41184158
if (!_M_Impl)
@@ -4149,7 +4189,7 @@ class task
41494189
_ContinuationTask._SetTaskCreationCallstack(_CreationStack);
41504190

41514191
_GetImpl()->_ScheduleContinuation(new _ContinuationTaskHandle<_InternalReturnType, _TaskType, _Function, typename _Function_type_traits::_Takes_task, typename _Async_type_traits::_AsyncKind>(
4152-
_GetImpl(), _ContinuationTask._GetImpl(), _Func, _ContinuationContext, _InliningMode));
4192+
_GetImpl(), _ContinuationTask._GetImpl(), std::forward<_Function>(_Func), _ContinuationContext, _InliningMode));
41534193

41544194
return _ContinuationTask;
41554195
}
@@ -4371,10 +4411,10 @@ class task<void>
43714411
/**/
43724412
template<typename _Function>
43734413
__declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result
4374-
auto then(const _Function& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType
4414+
auto then(_Function&& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType
43754415
{
43764416
details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK());
4377-
return _M_unitTask._ThenImpl<void, _Function>(_Func, _TaskOptions);
4417+
return _M_unitTask._ThenImpl<void, _Function>(std::forward<_Function>(_Func), _TaskOptions);
43784418
}
43794419

43804420
/// <summary>
@@ -4407,11 +4447,11 @@ class task<void>
44074447
/**/
44084448
template<typename _Function>
44094449
__declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result
4410-
auto then(const _Function& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType
4450+
auto then(_Function&& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType
44114451
{
44124452
task_options _TaskOptions(_CancellationToken, _ContinuationContext);
44134453
details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK());
4414-
return _M_unitTask._ThenImpl<void, _Function>(_Func, _TaskOptions);
4454+
return _M_unitTask._ThenImpl<void, _Function>(std::forward<_Function>(_Func), _TaskOptions);
44154455
}
44164456

44174457
/// <summary>
@@ -4555,13 +4595,13 @@ class task<void>
45554595
/// An internal version of then that takes additional flags and executes the continuation inline. Used for runtime internal continuations only.
45564596
/// </summary>
45574597
template<typename _Function>
4558-
auto _Then(const _Function& _Func, details::_CancellationTokenState *_PTokenState,
4598+
auto _Then(_Function&& _Func, details::_CancellationTokenState *_PTokenState,
45594599
details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType
45604600
{
45614601
// inherit from antecedent
45624602
auto _Scheduler = _GetImpl()->_GetScheduler();
45634603

4564-
return _M_unitTask._ThenImpl<void, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode);
4604+
return _M_unitTask._ThenImpl<void, _Function>(std::forward<_Function>(_Func), _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode);
45654605
}
45664606

45674607
private:

Release/tests/functional/pplx/pplx_test/pplxtask_tests.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,21 @@ TEST(TestTasks_void_tasks_default_construction)
304304
}
305305
}
306306

307+
TEST(TestTasks_movable_then)
308+
{
309+
struct A
310+
{
311+
A() = default;
312+
A(A&&) = default;
313+
A& operator=(A&&) = default;
314+
} a;
315+
316+
task<int> task = create_task([]{ return 2; });
317+
auto f = task.then([a = std::move(a)](int) { return 'c'; });
318+
319+
IsTrue(f.get() == 'c', L".then should be able to work with movable functors");
320+
}
321+
307322
TEST(TestTasks_constant_this)
308323
{
309324
#ifdef _MSC_VER

0 commit comments

Comments
 (0)