// @(#)root/thread:$Id$ // Author: Xavier Valls November 2017 /************************************************************************* * Copyright (C) 1995-2006, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ #ifndef ROOT_TSequentialExecutor #define ROOT_TSequentialExecutor #include "RConfigure.h" #include "ROOT/TExecutor.hxx" #include <numeric> #include <vector> namespace ROOT { class TSequentialExecutor: public TExecutor<TSequentialExecutor> { public: explicit TSequentialExecutor(){}; TSequentialExecutor(TSequentialExecutor &) = delete; TSequentialExecutor &operator=(TSequentialExecutor &) = delete; template<class F> void Foreach(F func, unsigned nTimes); template<class F, class INTEGER> void Foreach(F func, ROOT::TSeq<INTEGER> args); /// \cond template<class F, class T> void Foreach(F func, std::initializer_list<T> args); /// \endcond template<class F, class T> void Foreach(F func, std::vector<T> &args); using TExecutor<TSequentialExecutor>::Map; template<class F, class Cond = noReferenceCond<F>> auto Map(F func, unsigned nTimes) -> std::vector<typename std::result_of<F()>::type>; template<class F, class INTEGER, class Cond = noReferenceCond<F, INTEGER>> auto Map(F func, ROOT::TSeq<INTEGER> args) -> std::vector<typename std::result_of<F(INTEGER)>::type>; template<class F, class T, class Cond = noReferenceCond<F, T>> auto Map(F func, std::vector<T> &args) -> std::vector<typename std::result_of<F(T)>::type>; // // MapReduce // // the late return types also check at compile-time whether redfunc is compatible with func, // // other than checking that func is compatible with the type of arguments. // // a static_assert check in TSequentialExecutor::Reduce is used to check that redfunc is compatible with the type returned by func using TExecutor<TSequentialExecutor>::MapReduce; template<class F, class R, class Cond = noReferenceCond<F>> auto MapReduce(F func, unsigned nTimes, R redfunc) -> typename std::result_of<F()>::type; template<class F, class T, class R, class Cond = noReferenceCond<F, T>> auto MapReduce(F func, std::vector<T> &args, R redfunc) -> typename std::result_of<F(T)>::type; using TExecutor<TSequentialExecutor>::Reduce; template<class T, class R> auto Reduce(const std::vector<T> &objs, R redfunc) -> decltype(redfunc(objs)); }; /************ TEMPLATE METHODS IMPLEMENTATION ******************/ ////////////////////////////////////////////////////////////////////////// /// Execute func (with no arguments) nTimes. /// Functions that take more than zero arguments can be executed (with /// fixed arguments) by wrapping them in a lambda or with std::bind. template<class F> void TSequentialExecutor::Foreach(F func, unsigned nTimes) { for (auto i = 0U; i < nTimes; ++i) func(); } ////////////////////////////////////////////////////////////////////////// /// Execute func, taking an element of a /// sequence as argument. template<class F, class INTEGER> void TSequentialExecutor::Foreach(F func, ROOT::TSeq<INTEGER> args) { for(auto i : args) func(i); } /// \cond ////////////////////////////////////////////////////////////////////////// /// Execute func, taking an element of a /// initializer_list as argument. template<class F, class T> void TSequentialExecutor::Foreach(F func, std::initializer_list<T> args) { std::vector<T> vargs(std::move(args)); Foreach(func, vargs); } /// \endcond ////////////////////////////////////////////////////////////////////////// /// Execute func, taking an element of an /// std::vector as argument. template<class F, class T> void TSequentialExecutor::Foreach(F func, std::vector<T> &args) { unsigned int nToProcess = args.size(); for(auto i: ROOT::TSeqI(nToProcess)) func(args[i]); } ////////////////////////////////////////////////////////////////////////// /// Execute func (with no arguments) nTimes. /// A vector containg executions' results is returned. /// Functions that take more than zero arguments can be executed (with /// fixed arguments) by wrapping them in a lambda or with std::bind. template<class F, class Cond> auto TSequentialExecutor::Map(F func, unsigned nTimes) -> std::vector<typename std::result_of<F()>::type> { using retType = decltype(func()); std::vector<retType> reslist(nTimes); for(auto i: ROOT::TSeqI(nTimes)) reslist[i] = func(); return reslist; } ////////////////////////////////////////////////////////////////////////// /// Execute func, taking an element of a /// sequence as argument. /// A vector containg executions' results is returned. template<class F, class INTEGER, class Cond> auto TSequentialExecutor::Map(F func, ROOT::TSeq<INTEGER> args) -> std::vector<typename std::result_of<F(INTEGER)>::type> { using retType = decltype(func(*args.begin())); std::vector<retType> reslist(args.size()); for(auto i: args) reslist[i] = func(i); return reslist; } ////////////////////////////////////////////////////////////////////////// /// Execute func, taking an element of an /// std::vector as argument. /// A vector containg executions' results is returned. // actual implementation of the Map method. all other calls with arguments eventually // call this one template<class F, class T, class Cond> auto TSequentialExecutor::Map(F func, std::vector<T> &args) -> std::vector<typename std::result_of<F(T)>::type> { // //check whether func is callable using retType = decltype(func(args.front())); unsigned int nToProcess = args.size(); std::vector<retType> reslist(nToProcess); for(auto i: ROOT::TSeqI(nToProcess)) reslist[i] = func(args[i]); return reslist; } template<class F, class R, class Cond> auto TSequentialExecutor::MapReduce(F func, unsigned nTimes, R redfunc) -> typename std::result_of<F()>::type { return Reduce(Map(func, nTimes), redfunc); } template<class F, class T, class R, class Cond> auto TSequentialExecutor::MapReduce(F func, std::vector<T> &args, R redfunc) -> typename std::result_of<F(T)>::type { return Reduce(Map(func, args), redfunc); } ////////////////////////////////////////////////////////////////////////// /// "Reduce" an std::vector into a single object by passing a /// function as the second argument defining the reduction operation. template<class T, class R> auto TSequentialExecutor::Reduce(const std::vector<T> &objs, R redfunc) -> decltype(redfunc(objs)) { // check we can apply reduce to objs static_assert(std::is_same<decltype(redfunc(objs)), T>::value, "redfunc does not have the correct signature"); return redfunc(objs); } } // namespace ROOT #endif