Logo ROOT  
Reference Guide
TExecutorCRTP.hxx
Go to the documentation of this file.
1// @(#)root/core/base:$Id$
2// Author: Xavier Valls November 2020
3
4/*************************************************************************
5 * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#ifndef ROOT_TExecutorCRTP
13#define ROOT_TExecutorCRTP
14
15#include "ROOT/TSeq.hxx"
16#include "ROOT/TypeTraits.hxx" // InvokeResult_t
17#include "TError.h"
18#include "TList.h"
19
20#include <initializer_list>
21#include <type_traits> //std::enable_if
22#include <utility> //std::move
23#include <vector>
24
25//////////////////////////////////////////////////////////////////////////
26///
27/// \class ROOT::TExecutorCRTP
28/// \brief This class defines an interface to execute the same task
29/// multiple times, possibly in parallel and with different arguments every
30/// time.
31///
32/// ###ROOT::TExecutorCRTP<SubC>::Map
33/// The two possible usages of the Map method are:\n
34/// * `Map(F func, unsigned nTimes)`: func is executed nTimes with no arguments
35/// * `Map(F func, T& args)`: func is executed on each element of the collection of arguments args
36///
37/// The Map function forwards the call to MapImpl, to be implemented by the child classes.
38///
39/// For either signature, func is executed as many times as needed by a pool of
40/// n workers, where n typically defaults to the number of available cores.\n
41/// A collection containing the result of each execution is returned.\n
42/// **Note:** the user is responsible for the deletion of any object that might
43/// be created upon execution of func, returned objects included. ROOT::TExecutorCRTP derived classes
44/// never delete what they return, they simply forget it.\n
45///
46/// \param func
47/// \parblock
48/// a callable object, such as a lambda expression, an std::function, a
49/// functor object or a function that takes zero arguments (for the first signature)
50/// or one (for the second signature).
51/// \endparblock
52/// \param args
53/// \parblock
54/// a standard vector, a ROOT::TSeq of integer type or an initializer list for the second signature.
55/// An integer only for the first.\n
56/// \endparblock
57///
58/// **Note:** in cases where the function to be executed takes more than
59/// zero/one argument but all are fixed except zero/one, the function can be wrapped
60/// in a lambda or via std::bind to give it the right signature.\n
61///
62/// #### Return value:
63/// An std::vector. The elements in the container
64/// will be the objects returned by func. The ordering of the elements corresponds to the ordering of
65/// the arguments.
66///
67/// ### ROOT::TExecutorCRTP<SubC>::Reduce
68/// These set of methods combine all elements from a std::vector into a single value.
69/// \param redfunc
70/// \parblock
71/// a callable object, such as a lambda expression, an std::function, a
72/// functor object or a function that takes an std::vector and combines all its elements into a single result.\n
73/// \endparblock
74/// \param [args]
75/// \parblock
76/// a standard vector\n
77/// \endparblock
78///
79/// ### ROOT::TExecutorCRTP<SubC>::MapReduce
80/// This set of methods behaves exactly like Map, but takes an additional
81/// function as a third argument. This function is applied to the set of
82/// objects returned by the corresponding Map execution to "squash" them
83/// into a single object. This function should be independent of the size of
84/// the vector returned by Map due to optimization of the number of chunks.
85///
86/// #### Examples:
87/// ~~~{.cpp}
88/// Generate 1 ten times and sum those tens
89/// root[] ROOT::TProcessExecutor pool; auto ten = pool.MapReduce([]() { return 1; }, 10, [](const std::vector<int> &v) { return std::accumulate(v.begin(), v.end(), 0); })
90/// root[] ROOT::TProcessExecutor pool; auto tenOnes = pool.Map([]() { return 1; }, 10); auto ten = Reduce([](const std::vector<int> &v) { return std::accumulate(v.begin(), v.end(), 0); }, tenOnes)
91///
92/// Create 10 histograms and merge them into one
93/// root[] ROOT::TThreadExecutor pool; auto hist = pool.MapReduce(CreateAndFillHists, 10, PoolUtils::ReduceObjects);
94///
95/// ~~~
96///
97//////////////////////////////////////////////////////////////////////////
98
99
100namespace ROOT {
101
102template<class SubC>
104
105 template <typename F, typename... Args>
107
108public:
109 TExecutorCRTP() = default;
110 TExecutorCRTP(const TExecutorCRTP &) = delete;
112
113 /// type definition in used in templated functions for not allowing mapping functions that return references.
114 /// The resulting vector elements must be assignable, references aren't.
115 template <class F, class... T>
117 std::enable_if_t<"Function can't return a reference" && !std::is_reference<InvokeResult_t<F, T...>>::value>;
118
119 // Map
120 // These trailing return types allow for a compile time check of compatibility between function signatures and args
121 template <class F, class Cond = noReferenceCond<F>>
122 auto Map(F func, unsigned nTimes) -> std::vector<InvokeResult_t<F>>;
123 template <class F, class INTEGER, class Cond = noReferenceCond<F, INTEGER>>
124 auto Map(F func, ROOT::TSeq<INTEGER> args) -> std::vector<InvokeResult_t<F, INTEGER>>;
125 template <class F, class T, class Cond = noReferenceCond<F, T>>
126 auto Map(F func, std::initializer_list<T> args) -> std::vector<InvokeResult_t<F, T>>;
127 template <class F, class T, class Cond = noReferenceCond<F, T>>
128 auto Map(F func, std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>>;
129 template <class F, class T, class Cond = noReferenceCond<F, T>>
130 auto Map(F func, const std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>>;
131
132 // MapReduce
133 // The trailing return types check at compile time that func is compatible with the type of the arguments.
134 // A static_assert check in TExecutorCRTP<SubC>::Reduce is used to check that redfunc is compatible with the type returned by func
135 template <class F, class R, class Cond = noReferenceCond<F>>
136 auto MapReduce(F func, unsigned nTimes, R redfunc) -> InvokeResult_t<F>;
137 template <class F, class INTEGER, class R, class Cond = noReferenceCond<F, INTEGER>>
139 template <class F, class T, class R, class Cond = noReferenceCond<F, T>>
140 auto MapReduce(F func, std::initializer_list<T> args, R redfunc) -> InvokeResult_t<F, T>;
141 template <class F, class T, class R, class Cond = noReferenceCond<F, T>>
142 auto MapReduce(F func, const std::vector<T> &args, R redfunc) -> InvokeResult_t<F, T>;
143 template <class F, class T, class R, class Cond = noReferenceCond<F, T>>
144 auto MapReduce(F func, std::vector<T> &args, R redfunc) -> InvokeResult_t<F, T>;
145 template<class F, class T,class Cond = noReferenceCond<F, T>>
146 T* MapReduce(F func, std::vector<T*> &args);
147 template<class F, class T,class Cond = noReferenceCond<F, T>>
148 T* MapReduce(F func, const std::vector<T*> &args);
149
150 template<class T> T* Reduce(const std::vector<T*> &mergeObjs);
151 template<class T, class R> auto Reduce(const std::vector<T> &objs, R redfunc) -> decltype(redfunc(objs));
152
153private:
154
155 SubC &Derived()
156 {
157 return *static_cast<SubC*>(this);
158 }
159
160 /// Implementation of the Map method, left to the derived classes
161 template <class F, class Cond = noReferenceCond<F>>
162 auto MapImpl(F func, unsigned nTimes) -> std::vector<InvokeResult_t<F>> = delete;
163 /// Implementation of the Map method, left to the derived classes
164 template <class F, class INTEGER, class Cond = noReferenceCond<F, INTEGER>>
165 auto MapImpl(F func, ROOT::TSeq<INTEGER> args) -> std::vector<InvokeResult_t<F, INTEGER>> = delete;
166 /// Implementation of the Map method, left to the derived classes
167 template <class F, class T, class Cond = noReferenceCond<F, T>>
168 auto MapImpl(F func, std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>> = delete;
169 /// Implementation of the Map method, left to the derived classes
170 template <class F, class T, class Cond = noReferenceCond<F, T>>
171 auto MapImpl(F func, const std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>> = delete;
172};
173
174//////////////////////////////////////////////////////////////////////////
175/// \brief Execute a function without arguments several times.
176///
177/// \param func Function to be executed.
178/// \param nTimes Number of times function should be called.
179/// \return A vector with the results of the function calls.
180/// Functions that take arguments can be executed (with
181/// fixed arguments) by wrapping them in a lambda or with std::bind.
182template <class SubC>
183template <class F, class Cond>
184auto TExecutorCRTP<SubC>::Map(F func, unsigned nTimes) -> std::vector<InvokeResult_t<F>>
185{
186 return Derived().MapImpl(func, nTimes);
187}
188
189//////////////////////////////////////////////////////////////////////////
190/// \brief Execute a function over a sequence of indexes.
191///
192/// \param func Function to be executed. Must take an element of the sequence passed assecond argument as a parameter.
193/// \param args Sequence of indexes to execute `func` on.
194/// \return A vector with the results of the function calls.
195template <class SubC>
196template <class F, class INTEGER, class Cond>
197auto TExecutorCRTP<SubC>::Map(F func, ROOT::TSeq<INTEGER> args) -> std::vector<InvokeResult_t<F, INTEGER>>
198{
199 return Derived().MapImpl(func, args);
200}
201
202//////////////////////////////////////////////////////////////////////////
203/// \brief Execute a function over the elements of an initializer_list.
204///
205/// \param func Function to be executed on the elements of the initializer_list passed as second parameter.
206/// \param args initializer_list for a vector to apply `func` on.
207/// \return A vector with the results of the function calls.
208template <class SubC>
209template <class F, class T, class Cond>
210auto TExecutorCRTP<SubC>::Map(F func, std::initializer_list<T> args) -> std::vector<InvokeResult_t<F, T>>
211{
212 std::vector<T> vargs(std::move(args));
213 const auto &reslist = Map(func, vargs);
214 return reslist;
215}
216
217//////////////////////////////////////////////////////////////////////////
218/// \brief Execute a function over the elements of a vector.
219///
220/// \param func Function to be executed on the elements of the vector passed as second parameter.
221/// \param args Vector of elements passed as an argument to `func`.
222/// \return A vector with the results of the function calls.
223template <class SubC>
224template <class F, class T, class Cond>
225auto TExecutorCRTP<SubC>::Map(F func, std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>>
226{
227 return Derived().MapImpl(func, args);
228}
229
230//////////////////////////////////////////////////////////////////////////
231/// \brief Execute a function over the elements of an immutable vector
232
233///
234/// \param func Function to be executed on the elements of the vector passed as second parameter.
235/// \param args Vector of elements passed as an argument to `func`.
236/// \return A vector with the results of the function calls.
237template <class SubC>
238template <class F, class T, class Cond>
239auto TExecutorCRTP<SubC>::Map(F func, const std::vector<T> &args) -> std::vector<InvokeResult_t<F, T>>
240{
241 return Derived().MapImpl(func, args);
242}
243
244//////////////////////////////////////////////////////////////////////////
245/// \brief Execute a function without arguments several times (Map) and accumulate the results into a single value (Reduce).
246///
247/// \param func Function to be executed.
248/// \param nTimes Number of times function should be called.
249/// \return A vector with the results of the function calls.
250/// \param redfunc Reduction function to combine the results of the calls to `func`. Must return the same type as `func`.
251/// \return A value result of "reducing" the vector returned by the Map operation into a single object.
252template <class SubC>
253template <class F, class R, class Cond>
254auto TExecutorCRTP<SubC>::MapReduce(F func, unsigned nTimes, R redfunc) -> InvokeResult_t<F>
255{
256 return Reduce(Map(func, nTimes), redfunc);
257}
258
259//////////////////////////////////////////////////////////////////////////
260/// \brief Execute a function over a sequence of indexes (Map) and accumulate the results into a single value (Reduce).
261///
262/// \param func Function to be executed. Must take an element of the sequence passed assecond argument as a parameter.
263/// \param args Sequence of indexes to execute `func` on.
264/// \param redfunc Reduction function to combine the results of the calls to `func`. Must return the same type as `func`.
265/// \return A value result of "reducing" the vector returned by the Map operation into a single object.
266template <class SubC>
267template <class F, class INTEGER, class R, class Cond>
269{
270 return Reduce(Map(func, args), redfunc);
271}
272
273//////////////////////////////////////////////////////////////////////////
274/// \brief Execute a function over the elements of an initializer_list (Map) and accumulate the results into a single value (Reduce).
275///
276/// \param func Function to be executed on the elements of the initializer_list passed as second parameter.
277/// \param args initializer_list for a vector to apply `func` on.
278/// \param redfunc Reduction function to combine the results of the calls to `func`. Must return the same type as `func`.
279/// \return A value result of "reducing" the vector returned by the Map operation into a single object.
280template <class SubC>
281template <class F, class T, class R, class Cond>
282auto TExecutorCRTP<SubC>::MapReduce(F func, std::initializer_list<T> args, R redfunc) -> InvokeResult_t<F, T>
283{
284 std::vector<T> vargs(std::move(args));
285 return Reduce(Map(func, vargs), redfunc);
286}
287
288//////////////////////////////////////////////////////////////////////////
289/// \brief Execute a function over the elements of a vector (Map) and accumulate the results into a single value (Reduce).
290///
291/// \param func Function to be executed on the elements of the vector passed as second parameter.
292/// \param args Vector of elements passed as an argument to `func`.
293/// \param redfunc Reduction function to combine the results of the calls to `func`. Must return the same type as `func`.
294/// \return A value result of "reducing" the vector returned by the Map operation into a single object.
295template <class SubC>
296template <class F, class T, class R, class Cond>
297auto TExecutorCRTP<SubC>::MapReduce(F func, std::vector<T> &args, R redfunc) -> InvokeResult_t<F, T>
298{
299 return Reduce(Map(func, args), redfunc);
300}
301
302//////////////////////////////////////////////////////////////////////////
303/// \brief Execute a function over the elements of an immutable vector (Map) and accumulate the results into a single value (Reduce).
304///
305/// \param func Function to be executed on the elements of the vector passed as second parameter.
306/// \param args Immutable vector of elements passed as an argument to `func`.
307/// \param redfunc Reduction function to combine the results of the calls to `func`. Must return the same type as `func`.
308/// \return A value result of "reducing" the vector returned by the Map operation into a single object.
309template <class SubC>
310template <class F, class T, class R, class Cond>
311auto TExecutorCRTP<SubC>::MapReduce(F func, const std::vector<T> &args, R redfunc) -> InvokeResult_t<F, T>
312{
313 return Reduce(Map(func, args), redfunc);
314}
315
316//////////////////////////////////////////////////////////////////////////
317/// \brief Execute a function over the TObject-inheriting elements of a vector (Map) and merge the objects into a single one (Reduce).
318///
319/// \param func Function to be executed on the elements of the vector passed as second parameter.
320/// \param args Vector of elements passed as an argument to `func`.
321/// \return A value result of "reducing" the vector returned by the Map operation into a single object.
322template<class SubC> template<class F, class T, class Cond>
323T* TExecutorCRTP<SubC>::MapReduce(F func, std::vector<T*> &args)
324{
325 return Reduce(Map(func, args));
326}
327
328//////////////////////////////////////////////////////////////////////////
329/// \brief Execute a function over the TObject-inheriting elements of an immutable vector (Map) and merge the objects into a single one (Reduce).
330///
331/// \param func Function to be executed on the elements of the vector passed as second parameter.
332/// \param args Immutable vector of elements passed as an argument to `func`.
333/// \return A value result of "reducing" the vector returned by the Map operation into a single object.
334template<class SubC> template<class F, class T, class Cond>
335T* TExecutorCRTP<SubC>::MapReduce(F func, const std::vector<T*> &args)
336{
337 return Reduce(Map(func, args));
338}
339
340//////////////////////////////////////////////////////////////////////////
341/// \brief "Reduce" an std::vector into a single object by using the object's Merge method.
342///
343/// \param mergeObjs A vector of ROOT objects implementing the Merge method
344/// \return An object result of merging the vector elements into one.
345template<class SubC> template<class T>
346T* TExecutorCRTP<SubC>::Reduce(const std::vector<T*> &mergeObjs)
347{
348 ROOT::MergeFunc_t merge = mergeObjs.front()->IsA()->GetMerge();
349 if(!merge) {
350 Error("TExecutorCRTP<SubC>::Reduce", "could not find merge method for the TObject\n. Aborting operation.");
351 return nullptr;
352 }
353
354 TList l;
355 for(unsigned i =1; i<mergeObjs.size(); i++){
356 l.Add(mergeObjs[i]);
357 }
358 // use clone to return a new object
359 auto retHist = dynamic_cast<T*>((mergeObjs.front())->Clone());
360 if (retHist) retHist->Merge(&l);
361 return retHist;
362}
363
364//////////////////////////////////////////////////////////////////////////
365/// \brief "Reduce" an std::vector into a single object by passing a
366/// function as the second argument defining the reduction operation.
367///
368/// \param objs A vector of elements to combine.
369/// \param redfunc Reduction function to combine the elements of the vector `objs`
370/// \return A value result of combining the vector elements into a single object of the same type.
371template<class SubC> template<class T, class R>
372auto TExecutorCRTP<SubC>::Reduce(const std::vector<T> &objs, R redfunc) -> decltype(redfunc(objs))
373{
374 // check we can apply reduce to objs
375 static_assert(std::is_same<decltype(redfunc(objs)), T>::value, "redfunc does not have the correct signature");
376 return redfunc(objs);
377}
378
379} // end namespace ROOT
380#endif
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition: TError.cxx:187
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
This class defines an interface to execute the same task multiple times, possibly in parallel and wit...
auto MapImpl(F func, std::vector< T > &args) -> std::vector< InvokeResult_t< F, T > >=delete
Implementation of the Map method, left to the derived classes.
ROOT::TypeTraits::InvokeResult_t< F, Args... > InvokeResult_t
auto MapReduce(F func, unsigned nTimes, R redfunc) -> InvokeResult_t< F >
Execute a function without arguments several times (Map) and accumulate the results into a single val...
auto MapReduce(F func, ROOT::TSeq< INTEGER > args, R redfunc) -> InvokeResult_t< F, INTEGER >
Execute a function over a sequence of indexes (Map) and accumulate the results into a single value (R...
auto MapReduce(F func, const std::vector< T > &args, R redfunc) -> InvokeResult_t< F, T >
Execute a function over the elements of an immutable vector (Map) and accumulate the results into a s...
auto Map(F func, std::initializer_list< T > args) -> std::vector< InvokeResult_t< F, T > >
Execute a function over the elements of an initializer_list.
T * MapReduce(F func, const std::vector< T * > &args)
Execute a function over the TObject-inheriting elements of an immutable vector (Map) and merge the ob...
auto MapImpl(F func, unsigned nTimes) -> std::vector< InvokeResult_t< F > >=delete
Implementation of the Map method, left to the derived classes.
std::enable_if_t<"Function can't return a reference" &&!std::is_reference< InvokeResult_t< F, T... > >::value > noReferenceCond
type definition in used in templated functions for not allowing mapping functions that return referen...
auto Map(F func, std::vector< T > &args) -> std::vector< InvokeResult_t< F, T > >
Execute a function over the elements of a vector.
TExecutorCRTP()=default
auto Map(F func, const std::vector< T > &args) -> std::vector< InvokeResult_t< F, T > >
Execute a function over the elements of an immutable vector.
TExecutorCRTP(const TExecutorCRTP &)=delete
auto Map(F func, ROOT::TSeq< INTEGER > args) -> std::vector< InvokeResult_t< F, INTEGER > >
Execute a function over a sequence of indexes.
auto Map(F func, unsigned nTimes) -> std::vector< InvokeResult_t< F > >
Execute a function without arguments several times.
auto MapReduce(F func, std::vector< T > &args, R redfunc) -> InvokeResult_t< F, T >
Execute a function over the elements of a vector (Map) and accumulate the results into a single value...
T * Reduce(const std::vector< T * > &mergeObjs)
"Reduce" an std::vector into a single object by using the object's Merge method.
T * MapReduce(F func, std::vector< T * > &args)
Execute a function over the TObject-inheriting elements of a vector (Map) and merge the objects into ...
auto MapReduce(F func, std::initializer_list< T > args, R redfunc) -> InvokeResult_t< F, T >
Execute a function over the elements of an initializer_list (Map) and accumulate the results into a s...
auto Reduce(const std::vector< T > &objs, R redfunc) -> decltype(redfunc(objs))
"Reduce" an std::vector into a single object by passing a function as the second argument defining th...
TExecutorCRTP & operator=(const TExecutorCRTP &)=delete
auto MapImpl(F func, const std::vector< T > &args) -> std::vector< InvokeResult_t< F, T > >=delete
Implementation of the Map method, left to the derived classes.
auto MapImpl(F func, ROOT::TSeq< INTEGER > args) -> std::vector< InvokeResult_t< F, INTEGER > >=delete
Implementation of the Map method, left to the derived classes.
A pseudo container class which is a generator of indices.
Definition: TSeq.hxx:67
std::result_of_t< F(Args...)> InvokeResult_t
An adapter for std::invoke_result that falls back to std::result_of if the former is not available.
Definition: TypeTraits.hxx:201
A doubly linked list.
Definition: TList.h:38
auto Map(Args &&... args)
Create new collection applying a callable to the elements of the input collection.
Definition: RVec.hxx:2045
#define F(x, y, z)
double T(double x)
Definition: ChebyshevPol.h:34
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
Long64_t(* MergeFunc_t)(void *, TCollection *, TFileMergeInfo *)
Definition: Rtypes.h:114
TLine l
Definition: textangle.C:4