MEL
Microthread & Execution library
InlineExecutor.h
1 #pragma once
2 /*
3  * SPDX-FileCopyrightText: 2022 Daniel Barrientos <danivillamanin@gmail.com>
4  *
5  * SPDX-License-Identifier: MIT
6  */
7 #include <execution/Executor.h>
8 
9 #include <mpl/TypeTraits.h>
10 namespace mel
11 {
12 
13  namespace execution
14  {
15  using namespace mel::execution;
17  {
18  };
25  template <> class Executor<InlineExecutionAgent>
26  {
27  };
28  namespace _private
29  {
30  template <class F, class TArg>
31  void _invokeInline( ExFuture<InlineExecutionAgent, TArg> fut,
32  std::exception_ptr& except, F&& f )
33  {
34  static_assert( std::is_invocable<F, TArg>::value,
35  "inlineExecutor::_invokeInline bad signature" );
36  if constexpr ( std::is_nothrow_invocable<F, TArg>::value )
37  {
38  f( fut.getValue().value() );
39  }
40  else
41  {
42  try
43  {
44  f( fut.getValue().value() );
45  }
46  catch ( ... )
47  {
48  if ( !except )
49  except = std::current_exception();
50  }
51  }
52  }
53  // void overload
54  template <class F>
55  void _invokeInline( ExFuture<InlineExecutionAgent, void> fut,
56  std::exception_ptr& except, F&& f )
57  {
58 
59  if constexpr ( std::is_nothrow_invocable<F>::value )
60  {
61  f();
62  }
63  else
64  {
65  try
66  {
67  f();
68  }
69  catch ( ... )
70  {
71  if ( !except )
72  except = std::current_exception();
73  }
74  }
75  }
76  template <class TArg, class F, class... FTypes>
77  void _invokeInline( ExFuture<InlineExecutionAgent, TArg> fut,
78  std::exception_ptr& except, F&& f,
79  FTypes&&... fs )
80  {
81  _invokeInline( fut, except, std::forward<F>( f ) );
82  _invokeInline( fut, except, std::forward<FTypes>( fs )... );
83  }
84  template <int n, class ResultTuple, class F, class TArg>
85  void
86  _invokeInline_with_result( ExFuture<InlineExecutionAgent, TArg> fut,
87  std::exception_ptr& except,
88  ResultTuple& output, F&& f )
89  {
90  static_assert(
91  std::is_invocable<F, TArg>::value,
92  "inlineExecutor::_invokeInline_with_result bad signature" );
93  if constexpr ( std::is_nothrow_invocable<F, TArg>::value )
94  {
95  if constexpr ( std::is_same<std::invoke_result_t<F, TArg>,
96  void>::value )
97  f( fut.getValue().value() );
98  else
99  std::get<n>( output ) = f( fut.getValue().value() );
100  }
101  else
102  {
103  try
104  {
105  if constexpr ( std::is_same<
106  std::invoke_result_t<F, TArg>,
107  void>::value )
108  f( fut.getValue().value() );
109  else
110  std::get<n>( output ) = f( fut.getValue().value() );
111  }
112  catch ( ... )
113  {
114  if ( !except )
115  except = std::current_exception();
116  }
117  }
118  }
119  // void overload
120  template <int n, class ResultTuple, class F>
121  void _invokeInline_with_result(
123  std::exception_ptr& except, ResultTuple& output, F&& f )
124  {
125  if constexpr ( std::is_nothrow_invocable<F>::value )
126  {
127  if constexpr ( std::is_same<std::invoke_result_t<F>,
128  void>::value )
129  f();
130  else
131  std::get<n>( output ) = f();
132  }
133  else
134  {
135  try
136  {
137  if constexpr ( std::is_same<std::invoke_result_t<F>,
138  void>::value )
139  f();
140  else
141  std::get<n>( output ) = f();
142  }
143  catch ( ... )
144  {
145  if ( !except )
146  except = std::current_exception();
147  }
148  }
149  }
150 
151  template <int n, class ResultTuple, class TArg, class F,
152  class... FTypes>
153  void
154  _invokeInline_with_result( ExFuture<InlineExecutionAgent, TArg> fut,
155  std::exception_ptr& except,
156  ResultTuple& output, F&& f,
157  FTypes&&... fs )
158  {
159  _invokeInline_with_result<n>( fut, except, output,
160  std::forward<F>( f ) );
161  _invokeInline_with_result<n + 1>(
162  fut, except, output, std::forward<FTypes>( fs )... );
163  }
164  } // namespace _private
165 
166  // overload for performance reasons
167  template <class TArg, class I, class F>
169  loop( ExFuture<InlineExecutionAgent, TArg> source, I&& getIteratorsFunc,
170  F&& functor, int increment = 1 )
171  {
172  if ( source.getValid() )
173  {
174  std::exception_ptr except{ nullptr };
175  auto& v = source.getValue().value();
176  auto iterators = getIteratorsFunc( v );
177  using IteratorType = decltype( iterators[0] );
178  static_assert( std::is_invocable<F, IteratorType, TArg>::value,
179  "InlineExecutor::loop bad signature" );
180  //@todo remember this iteration could not valid for increment
181  // diferent to 1 and not arithmetic iterators
182  if constexpr ( std::is_nothrow_invocable<F, IteratorType,
183  TArg>::value )
184  {
185  for ( auto i = iterators[0]; i != iterators[1];
186  i += increment )
187  {
188  functor( i, v );
189  }
190  }
191  else
192  {
193  for ( auto i = iterators[0]; i != iterators[1];
194  i += increment )
195  {
196  try
197  {
198  functor( i, v );
199  }
200  catch ( ... )
201  {
202  if ( !except )
203  except = std::current_exception();
204  }
205  }
206  }
207  if ( !except )
209  v );
210  else
211  {
212  auto result =
214  result.setError( except );
215  return result;
216  }
217  }
218  else
219  {
220  auto result =
222  result.setError( source.getValue().error() );
223  return result;
224  }
225  }
226  // voide overload for performance reasons
227  template <class I, class F>
229  loop( ExFuture<InlineExecutionAgent, void> source, I&& getIteratorsFunc,
230  F&& functor, int increment = 1 )
231  {
232  if ( source.getValid() )
233  {
234  std::exception_ptr except{ nullptr };
235  auto iterators = getIteratorsFunc();
236  using IteratorType = decltype( iterators[0] );
237  if constexpr ( std::is_nothrow_invocable<F,
238  IteratorType>::value )
239  {
240  for ( auto i = iterators[0]; i != iterators[1];
241  i += increment )
242  {
243  functor( i );
244  }
245  }
246  else
247  {
248  for ( auto i = iterators[0]; i != iterators[1];
249  i += increment )
250  {
251  try
252  {
253  functor( i );
254  }
255  catch ( ... )
256  {
257  if ( !except )
258  except = std::current_exception();
259  }
260  }
261  }
262  if ( !except )
264  1 );
265  else
266  {
267  auto result =
269  result.setError( except );
270  return result;
271  }
272  }
273  else
274  {
275  auto result =
277  result.setError( source.getValue().error() );
278  return result;
279  }
280  }
281  template <class TArg, class... FTypes>
284  FTypes&&... functions )
285  {
286  std::exception_ptr except{ nullptr };
287  if ( source.getValid() )
288  {
289  _private::_invokeInline( source, except,
290  std::forward<FTypes>( functions )... );
291  if ( !except )
293  source.agent, source.getValue().value() );
294  else
295  {
296  auto result =
298  result.setError( except );
299  return result;
300  }
301  }
302  else
303  {
304  auto result =
306  result.setError( source.getValue().error() );
307  return result;
308  }
309  }
310  /*
312  template <class ...FTypes> ExFuture<InlineExecutionAgent,void>
313  parallel(ExFuture<InlineExecutionAgent,void> source, FTypes&&...
314  functions)
315  {
316  std::exception_ptr except{nullptr};
317  if ( source.getValid() )
318  {
319  _private::_invokeInline(source,except,std::forward<FTypes>(functions)...);
320  if ( !except)
321  return ExFuture<InlineExecutionAgent,void>(source.agent,1);
322  else
323  {
324  auto result =
325  ExFuture<InlineExecutionAgent,void>(source.agent);
326  result.setError(except);
327  return result;
328  }
329  }
330  else
331  {
332  auto result = ExFuture<InlineExecutionAgent,void>(source.agent);
333  result.setError(source.getValue().error());
334  return result;
335  }
336  }
337  */
338  // overload for performance reasons
339  template <class TArg, class... FTypes>
340  ExFuture<
342  typename mel::execution::_private::GetReturn<TArg, FTypes...>::type>
344  FTypes&&... functions )
345  {
346  typedef typename mel::execution::_private::GetReturn<
347  TArg, FTypes...>::type ResultTuple;
348  std::exception_ptr except{ nullptr };
349  if ( source.getValid() )
350  {
352  source.agent );
353  ResultTuple resultTuple;
354  _private::_invokeInline_with_result<0>(
355  source, except, resultTuple,
356  std::forward<FTypes>( functions )... );
357  if ( !except )
358  result.setValue( std::move( resultTuple ) );
359  else
360  {
361  result.setError( except );
362  }
363  return result;
364  }
365  else
366  {
367  auto result =
369  result.setError( source.getValue().error() );
370  return result;
371  }
372  }
373 
378  template <class F>
381  {
382  typedef std::invoke_result_t<F> TRet;
383 
384  if constexpr ( std::is_same<TRet, void>::value )
385  {
386  if constexpr ( std::is_nothrow_invocable<F>::value )
387  {
388  f();
389  return ExFuture<InlineExecutionAgent, TRet>( ex, 1 );
390  }
391  else
392  {
393  try
394  {
395  f();
396  return ExFuture<InlineExecutionAgent, TRet>( ex, 1 );
397  }
398  catch ( ... )
399  {
400  auto result =
402  result.setError( std::current_exception() );
403  return result;
404  }
405  }
406  }
407  else
408  {
409  if constexpr ( std::is_nothrow_invocable<F>::value )
410  return ExFuture<InlineExecutionAgent, TRet>( ex, f() );
411  else
412  {
413  try
414  {
415  return ExFuture<InlineExecutionAgent, TRet>( ex, f() );
416  }
417  catch ( ... )
418  {
419  auto result =
421  result.setError( std::current_exception() );
422  return result;
423  }
424  }
425  }
426  }
432  template <class TArg, class F>
434  launch( Executor<InlineExecutionAgent> ex, F&& f, TArg&& arg )
435  {
436  /*
437  @todo I need to mature this idea. It's not so transparent to add
438  reference check but same rules as for "inmediate" should be followed
439  static_assert( !std::is_lvalue_reference<TArg>::value ||
440  std::is_const< typename
441  std::remove_reference<TArg>::type>::value,"execution::launch. Use
442  std::ref() to pass argument as reference");
443  */
444  static_assert( std::is_invocable<F, TArg>::value,
445  "InlineExecutor::launch. Bad signature" );
446  typedef std::invoke_result_t<F, TArg> TRet;
447  // return
448  // ExFuture<InlineExecutionAgent,TRet>(ex,f(std::forward<TArg>(arg)));
449  if constexpr ( std::is_same<TRet, void>::value )
450  {
451  if constexpr ( std::is_nothrow_invocable<F, TArg>::value )
452  {
453  f( std::forward<TArg>( arg ) );
454  return ExFuture<InlineExecutionAgent, TRet>( ex, 1 );
455  }
456  else
457  {
458  try
459  {
460  f( std::forward<TArg>( arg ) );
461  return ExFuture<InlineExecutionAgent, TRet>( ex, 1 );
462  }
463  catch ( ... )
464  {
465  auto result =
467  result.setError( std::current_exception() );
468  return result;
469  }
470  }
471  }
472  else
473  {
474  if constexpr ( std::is_nothrow_invocable<F, TArg>::value )
476  ex, f( std::forward<TArg>( arg ) ) );
477  else
478  {
479  try
480  {
482  ex, f( std::forward<TArg>( arg ) ) );
483  }
484  catch ( ... )
485  {
486  auto result =
488  result.setError( std::current_exception() );
489  return result;
490  }
491  }
492  }
493  }
494 
495  // reimplementation of base next for InlineExecutionAgent to improve
496  // performance compared to NaiveInlineExecutor
497  template <class F, class TArg>
500  {
501  static_assert( std::is_invocable<F, TArg>::value,
502  "InlineExecutor::next. Bad signature" );
503  typedef std::invoke_result_t<F, TArg> TRet;
504  if ( source.getValid() )
505  {
506  if constexpr ( std::is_same<TRet, void>::value )
507  {
508  if constexpr ( std::is_nothrow_invocable<F, TArg>::value )
509  {
510  f( source.getValue().value() );
512  source.agent, 1 );
513  }
514  else
515  {
516  try
517  {
518  f( source.getValue().value() );
520  source.agent, 1 );
521  }
522  catch ( ... )
523  {
524  // output.setError(std::current_exception());
526  source.agent );
527  result.setError( std::current_exception() );
528  return result;
529  }
530  }
531  }
532  else
533  {
534  if constexpr ( std::is_nothrow_invocable<F, TArg>::value )
535  {
537  source.agent, f( source.getValue().value() ) );
538  }
539  else
540  {
541  try
542  {
544  source.agent, f( source.getValue().value() ) );
545  }
546  catch ( ... )
547  {
548  // output.setError(std::current_exception());
550  source.agent );
551  result.setError( std::current_exception() );
552  return result;
553  }
554  }
555  }
556  }
557  else
558  {
559  // return
560  // ExFuture<InlineExecutionAgent,TRet>(source.agent,source.getValue().error());
561  auto result =
563  result.setError( source.getValue().error() );
564  return result;
565  }
566  }
567  // void overload
568  template <class F>
571  {
572  typedef std::invoke_result_t<F> TRet;
573  if ( source.getValid() )
574  {
575  if constexpr ( std::is_same<TRet, void>::value )
576  {
577  if constexpr ( std::is_nothrow_invocable<F>::value )
578  {
579  f();
581  source.agent, 1 );
582  }
583  else
584  {
585  try
586  {
587  f();
589  source.agent, 1 );
590  }
591  catch ( ... )
592  {
593  // output.setError(std::current_exception());
595  source.agent );
596  result.setError( std::current_exception() );
597  return result;
598  }
599  }
600  }
601  else
602  {
603  if constexpr ( std::is_nothrow_invocable<F>::value )
604  {
606  source.agent, f() );
607  }
608  else
609  {
610  try
611  {
613  source.agent, f() );
614  }
615  catch ( ... )
616  {
617  // output.setError(std::current_exception());
619  source.agent );
620  result.setError( std::current_exception() );
621  return result;
622  }
623  }
624  }
625  }
626  else
627  {
628  // return
629  // ExFuture<InlineExecutionAgent,TRet>(source.agent,source.getValue().error());
630  auto result =
632  result.setError( source.getValue().error() );
633  return result;
634  }
635  }
636 
637  // inmediate overload for performance reasons
638  template <class TArg, class TRet>
640  typename std::remove_cv<
641  typename std::remove_reference<TRet>::type>::type>
643  {
644  static_assert(
645  !std::is_lvalue_reference<TRet>::value ||
646  std::is_const<
647  typename std::remove_reference<TRet>::type>::value,
648  "execution::inmediate. Use std::ref() to pass argument as "
649  "reference" );
650  using NewType = typename std::remove_cv<
651  typename std::remove_reference<TRet>::type>::type;
653  fut.agent, std::forward<TRet>( arg ) );
654  }
655 
659  template <>
661  : ExecutorTraits<void> // inherit same default traits
662  {
663  };
666  } // namespace execution
667 } // namespace mel
Typical Traits for types.
const _private::FutureData< T >::ValueType & getValue() const
Get the Value object.
Definition: Future.h:613
Extension of mel::core::Future to apply to executors.
Definition: ExFuture.h:21
Executor< ExecutorAgent > agent
execution agent associated with this instance
Definition: ExFuture.h:54
Executor specialization using InlineExecutionAgent as execution agent.
Definition: InlineExecutor.h:26
Definition: Executor.h:26
High level execution utilities.
Definition: CommonDefs.h:10
ExFuture< ExecutorAgent, typename std::remove_cv< typename std::remove_reference< TRet >::type >::type > inmediate(ExFuture< ExecutorAgent, TArg > fut, TRet &&arg)
Produces an inmediate value in the context of the given ExFuture executor as a response to input fut ...
Definition: Executor.h:117
ExFuture< ExecutorAgent, std::invoke_result_t< F > > launch(Executor< ExecutorAgent > ex, F &&f)
Launch given functor in given executor.
Definition: Executor.h:65
ExFuture< ExecutorAgent, TArg > parallel(ExFuture< ExecutorAgent, TArg > source, FTypes... functions)
Execute given functions in a (possibly, depending on concrete executor) parallel way If an exception ...
Definition: Executor.h:499
ExFuture< ExecutorAgent, TArg > loop(ExFuture< ExecutorAgent, TArg > source, I getIteratorsFunc, F functor, int increment=1)
parallel (possibly, depending on executor capabilities) loop
Definition: Executor.h:290
ExFuture< ExecutorAgent, typename ::mel::execution::_private::GetReturn< TArg, FTypes... >::type > parallel_convert(ExFuture< ExecutorAgent, TArg > source, FTypes... functions)
Same as parallel but returning a tuple with the values for each functor.
Definition: Executor.h:726
ExFuture< ExecutorAgent, std::invoke_result_t< F, TArg > > next(ExFuture< ExecutorAgent, TArg > source, F f)
Attach a functor to execute when input fut is complete Given functor will be executed inf the input E...
Definition: Executor.h:166
Executor< InlineExecutionAgent > InlineExecutor
alias for Executor<InlineExecutionAgent>
Definition: InlineExecutor.h:665
Definition: Callback_Impl.h:11
Default traits for any executor.
Definition: Executor.h:46
Definition: InlineExecutor.h:17