Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-11-15 09:55:42

0001 // Author: Enrico Guiraud, Danilo Piparo CERN  02/2018
0002 
0003 /*************************************************************************
0004  * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.               *
0005  * All rights reserved.                                                  *
0006  *                                                                       *
0007  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0008  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0009  *************************************************************************/
0010 
0011 #ifndef ROOT_RDF_TINTERFACE_UTILS
0012 #define ROOT_RDF_TINTERFACE_UTILS
0013 
0014 #include "RColumnRegister.hxx"
0015 #include <ROOT/RDF/RAction.hxx>
0016 #include <ROOT/RDF/ActionHelpers.hxx> // for BuildAction
0017 #include <ROOT/RDF/RColumnRegister.hxx>
0018 #include <ROOT/RDF/RDefine.hxx>
0019 #include <ROOT/RDF/RDefinePerSample.hxx>
0020 #include <ROOT/RDF/RFilter.hxx>
0021 #include <ROOT/RDF/Utils.hxx>
0022 #include <ROOT/RDF/RJittedAction.hxx>
0023 #include <ROOT/RDF/RJittedDefine.hxx>
0024 #include <ROOT/RDF/RJittedFilter.hxx>
0025 #include <ROOT/RDF/RJittedVariation.hxx>
0026 #include <ROOT/RDF/RLoopManager.hxx>
0027 #include <ROOT/RStringView.hxx>
0028 #include <ROOT/RDF/RVariation.hxx>
0029 #include <ROOT/TypeTraits.hxx>
0030 #include <TError.h> // gErrorIgnoreLevel
0031 #include <TH1.h>
0032 #include <TROOT.h> // IsImplicitMTEnabled
0033 
0034 #include <deque>
0035 #include <functional>
0036 #include <map>
0037 #include <memory>
0038 #include <string>
0039 #include <type_traits>
0040 #include <typeinfo>
0041 #include <vector>
0042 #include <unordered_map>
0043 
0044 class TObjArray;
0045 class TTree;
0046 namespace ROOT {
0047 namespace Detail {
0048 namespace RDF {
0049 class RNodeBase;
0050 }
0051 }
0052 namespace RDF {
0053 template <typename T>
0054 class RResultPtr;
0055 template<typename T, typename V>
0056 class RInterface;
0057 using RNode = RInterface<::ROOT::Detail::RDF::RNodeBase, void>;
0058 class RDataSource;
0059 } // namespace RDF
0060 
0061 } // namespace ROOT
0062 
0063 /// \cond HIDDEN_SYMBOLS
0064 
0065 namespace ROOT {
0066 namespace Internal {
0067 namespace RDF {
0068 using namespace ROOT::Detail::RDF;
0069 using namespace ROOT::RDF;
0070 namespace TTraits = ROOT::TypeTraits;
0071 
0072 std::string DemangleTypeIdName(const std::type_info &typeInfo);
0073 
0074 ColumnNames_t
0075 ConvertRegexToColumns(const ColumnNames_t &colNames, std::string_view columnNameRegexp, std::string_view callerName);
0076 
0077 /// An helper object that sets and resets gErrorIgnoreLevel via RAII.
0078 class RIgnoreErrorLevelRAII {
0079 private:
0080    int fCurIgnoreErrorLevel = gErrorIgnoreLevel;
0081 
0082 public:
0083    RIgnoreErrorLevelRAII(int errorIgnoreLevel) { gErrorIgnoreLevel = errorIgnoreLevel; }
0084    ~RIgnoreErrorLevelRAII() { gErrorIgnoreLevel = fCurIgnoreErrorLevel; }
0085 };
0086 
0087 /****** BuildAction overloads *******/
0088 
0089 // clang-format off
0090 /// This namespace defines types to be used for tag dispatching in RInterface.
0091 namespace ActionTags {
0092 struct Histo1D{};
0093 struct Histo2D{};
0094 struct Histo3D{};
0095 struct HistoND{};
0096 struct Graph{};
0097 struct GraphAsymmErrors{};
0098 struct Profile1D{};
0099 struct Profile2D{};
0100 struct Min{};
0101 struct Max{};
0102 struct Sum{};
0103 struct Mean{};
0104 struct Fill{};
0105 struct StdDev{};
0106 struct Display{};
0107 struct Snapshot{};
0108 struct Book{};
0109 }
0110 // clang-format on
0111 
0112 template <typename T, bool ISV6HISTO = std::is_base_of<TH1, std::decay_t<T>>::value>
0113 struct HistoUtils {
0114    static void SetCanExtendAllAxes(T &h) { h.SetCanExtend(::TH1::kAllAxes); }
0115    static bool HasAxisLimits(T &h)
0116    {
0117       auto xaxis = h.GetXaxis();
0118       return !(xaxis->GetXmin() == 0. && xaxis->GetXmax() == 0.);
0119    }
0120 };
0121 
0122 template <typename T>
0123 struct HistoUtils<T, false> {
0124    static void SetCanExtendAllAxes(T &) {}
0125    static bool HasAxisLimits(T &) { return true; }
0126 };
0127 
0128 // Generic filling (covers Histo2D, Histo3D, HistoND, Profile1D and Profile2D actions, with and without weights)
0129 template <typename... ColTypes, typename ActionTag, typename ActionResultType, typename PrevNodeType>
0130 std::unique_ptr<RActionBase>
0131 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<ActionResultType> &h, const unsigned int nSlots,
0132             std::shared_ptr<PrevNodeType> prevNode, ActionTag, const RColumnRegister &colRegister)
0133 {
0134    using Helper_t = FillHelper<ActionResultType>;
0135    using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColTypes...>>;
0136    return std::make_unique<Action_t>(Helper_t(h, nSlots), bl, std::move(prevNode), colRegister);
0137 }
0138 
0139 // Histo1D filling (must handle the special case of distinguishing FillHelper and BufferedFillHelper
0140 template <typename... ColTypes, typename PrevNodeType>
0141 std::unique_ptr<RActionBase>
0142 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<::TH1D> &h, const unsigned int nSlots,
0143             std::shared_ptr<PrevNodeType> prevNode, ActionTags::Histo1D, const RColumnRegister &colRegister)
0144 {
0145    auto hasAxisLimits = HistoUtils<::TH1D>::HasAxisLimits(*h);
0146 
0147    if (hasAxisLimits || !IsImplicitMTEnabled()) {
0148       using Helper_t = FillHelper<::TH1D>;
0149       using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColTypes...>>;
0150       return std::make_unique<Action_t>(Helper_t(h, nSlots), bl, std::move(prevNode), colRegister);
0151    } else {
0152       using Helper_t = BufferedFillHelper;
0153       using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColTypes...>>;
0154       return std::make_unique<Action_t>(Helper_t(h, nSlots), bl, std::move(prevNode), colRegister);
0155    }
0156 }
0157 
0158 template <typename... ColTypes, typename PrevNodeType>
0159 std::unique_ptr<RActionBase>
0160 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<TGraph> &g, const unsigned int nSlots,
0161             std::shared_ptr<PrevNodeType> prevNode, ActionTags::Graph, const RColumnRegister &colRegister)
0162 {
0163    using Helper_t = FillTGraphHelper;
0164    using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColTypes...>>;
0165    return std::make_unique<Action_t>(Helper_t(g, nSlots), bl, std::move(prevNode), colRegister);
0166 }
0167 
0168 template <typename... ColTypes, typename PrevNodeType>
0169 std::unique_ptr<RActionBase>
0170 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<TGraphAsymmErrors> &g, const unsigned int nSlots,
0171             std::shared_ptr<PrevNodeType> prevNode, ActionTags::GraphAsymmErrors, const RColumnRegister &colRegister)
0172 {
0173    using Helper_t = FillTGraphAsymmErrorsHelper;
0174    using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColTypes...>>;
0175    return std::make_unique<Action_t>(Helper_t(g, nSlots), bl, std::move(prevNode), colRegister);
0176 }
0177 
0178 // Min action
0179 template <typename ColType, typename PrevNodeType, typename ActionResultType>
0180 std::unique_ptr<RActionBase>
0181 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<ActionResultType> &minV, const unsigned int nSlots,
0182             std::shared_ptr<PrevNodeType> prevNode, ActionTags::Min, const RColumnRegister &colRegister)
0183 {
0184    using Helper_t = MinHelper<ActionResultType>;
0185    using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColType>>;
0186    return std::make_unique<Action_t>(Helper_t(minV, nSlots), bl, std::move(prevNode), colRegister);
0187 }
0188 
0189 // Max action
0190 template <typename ColType, typename PrevNodeType, typename ActionResultType>
0191 std::unique_ptr<RActionBase>
0192 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<ActionResultType> &maxV, const unsigned int nSlots,
0193             std::shared_ptr<PrevNodeType> prevNode, ActionTags::Max, const RColumnRegister &colRegister)
0194 {
0195    using Helper_t = MaxHelper<ActionResultType>;
0196    using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColType>>;
0197    return std::make_unique<Action_t>(Helper_t(maxV, nSlots), bl, std::move(prevNode), colRegister);
0198 }
0199 
0200 // Sum action
0201 template <typename ColType, typename PrevNodeType, typename ActionResultType>
0202 std::unique_ptr<RActionBase>
0203 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<ActionResultType> &sumV, const unsigned int nSlots,
0204             std::shared_ptr<PrevNodeType> prevNode, ActionTags::Sum, const RColumnRegister &colRegister)
0205 {
0206    using Helper_t = SumHelper<ActionResultType>;
0207    using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColType>>;
0208    return std::make_unique<Action_t>(Helper_t(sumV, nSlots), bl, std::move(prevNode), colRegister);
0209 }
0210 
0211 // Mean action
0212 template <typename ColType, typename PrevNodeType>
0213 std::unique_ptr<RActionBase>
0214 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<double> &meanV, const unsigned int nSlots,
0215             std::shared_ptr<PrevNodeType> prevNode, ActionTags::Mean, const RColumnRegister &colRegister)
0216 {
0217    using Helper_t = MeanHelper;
0218    using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColType>>;
0219    return std::make_unique<Action_t>(Helper_t(meanV, nSlots), bl, std::move(prevNode), colRegister);
0220 }
0221 
0222 // Standard Deviation action
0223 template <typename ColType, typename PrevNodeType>
0224 std::unique_ptr<RActionBase>
0225 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<double> &stdDeviationV, const unsigned int nSlots,
0226             std::shared_ptr<PrevNodeType> prevNode, ActionTags::StdDev, const RColumnRegister &colRegister)
0227 {
0228    using Helper_t = StdDevHelper;
0229    using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColType>>;
0230    return std::make_unique<Action_t>(Helper_t(stdDeviationV, nSlots), bl, prevNode, colRegister);
0231 }
0232 
0233 using displayHelperArgs_t = std::pair<size_t, std::shared_ptr<ROOT::RDF::RDisplay>>;
0234 
0235 // Display action
0236 template <typename... ColTypes, typename PrevNodeType>
0237 std::unique_ptr<RActionBase>
0238 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<displayHelperArgs_t> &helperArgs, const unsigned int,
0239             std::shared_ptr<PrevNodeType> prevNode, ActionTags::Display, const RColumnRegister &colRegister)
0240 {
0241    using Helper_t = DisplayHelper<PrevNodeType>;
0242    using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColTypes...>>;
0243    return std::make_unique<Action_t>(Helper_t(helperArgs->first, helperArgs->second, prevNode), bl, prevNode,
0244                                      colRegister);
0245 }
0246 
0247 struct SnapshotHelperArgs {
0248    std::string fFileName;
0249    std::string fDirName;
0250    std::string fTreeName;
0251    std::vector<std::string> fOutputColNames;
0252    ROOT::RDF::RSnapshotOptions fOptions;
0253 };
0254 
0255 // Snapshot action
0256 template <typename... ColTypes, typename PrevNodeType>
0257 std::unique_ptr<RActionBase>
0258 BuildAction(const ColumnNames_t &colNames, const std::shared_ptr<SnapshotHelperArgs> &snapHelperArgs,
0259             const unsigned int nSlots, std::shared_ptr<PrevNodeType> prevNode, ActionTags::Snapshot,
0260             const RColumnRegister &colRegister)
0261 {
0262    const auto &filename = snapHelperArgs->fFileName;
0263    const auto &dirname = snapHelperArgs->fDirName;
0264    const auto &treename = snapHelperArgs->fTreeName;
0265    const auto &outputColNames = snapHelperArgs->fOutputColNames;
0266    const auto &options = snapHelperArgs->fOptions;
0267 
0268    auto makeIsDefine = [&] {
0269       std::vector<bool> isDef;
0270       isDef.reserve(sizeof...(ColTypes));
0271       for (auto i = 0u; i < sizeof...(ColTypes); ++i)
0272          isDef.push_back(colRegister.IsDefineOrAlias(colNames[i]));
0273       return isDef;
0274    };
0275    std::vector<bool> isDefine = makeIsDefine();
0276 
0277    std::unique_ptr<RActionBase> actionPtr;
0278    if (!ROOT::IsImplicitMTEnabled()) {
0279       // single-thread snapshot
0280       using Helper_t = SnapshotHelper<ColTypes...>;
0281       using Action_t = RAction<Helper_t, PrevNodeType>;
0282       actionPtr.reset(
0283          new Action_t(Helper_t(filename, dirname, treename, colNames, outputColNames, options, std::move(isDefine)),
0284                       colNames, prevNode, colRegister));
0285    } else {
0286       // multi-thread snapshot
0287       using Helper_t = SnapshotHelperMT<ColTypes...>;
0288       using Action_t = RAction<Helper_t, PrevNodeType>;
0289       actionPtr.reset(new Action_t(
0290          Helper_t(nSlots, filename, dirname, treename, colNames, outputColNames, options, std::move(isDefine)),
0291          colNames, prevNode, colRegister));
0292    }
0293    return actionPtr;
0294 }
0295 
0296 // Book with custom helper type
0297 template <typename... ColTypes, typename PrevNodeType, typename Helper_t>
0298 std::unique_ptr<RActionBase>
0299 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<Helper_t> &h, const unsigned int /*nSlots*/,
0300             std::shared_ptr<PrevNodeType> prevNode, ActionTags::Book, const RColumnRegister &colRegister)
0301 {
0302    using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColTypes...>>;
0303    return std::make_unique<Action_t>(Helper_t(std::move(*h)), bl, std::move(prevNode), colRegister);
0304 }
0305 
0306 /****** end BuildAndBook ******/
0307 
0308 template <typename Filter>
0309 void CheckFilter(Filter &)
0310 {
0311    using FilterRet_t = typename RDF::CallableTraits<Filter>::ret_type;
0312    static_assert(std::is_convertible<FilterRet_t, bool>::value,
0313                  "filter expression returns a type that is not convertible to bool");
0314 }
0315 
0316 ColumnNames_t FilterArraySizeColNames(const ColumnNames_t &columnNames, const std::string &action);
0317 
0318 void CheckValidCppVarName(std::string_view var, const std::string &where);
0319 
0320 void CheckForRedefinition(const std::string &where, std::string_view definedCol, const RColumnRegister &colRegister,
0321                           const ColumnNames_t &treeColumns, const ColumnNames_t &dataSourceColumns);
0322 
0323 void CheckForDefinition(const std::string &where, std::string_view definedColView, const RColumnRegister &colRegister,
0324                         const ColumnNames_t &treeColumns, const ColumnNames_t &dataSourceColumns);
0325 
0326 void CheckForNoVariations(const std::string &where, std::string_view definedColView,
0327                           const RColumnRegister &colRegister);
0328 
0329 std::string PrettyPrintAddr(const void *const addr);
0330 
0331 std::shared_ptr<RJittedFilter> BookFilterJit(std::shared_ptr<RNodeBase> *prevNodeOnHeap, std::string_view name,
0332                                              std::string_view expression, const ColumnNames_t &branches,
0333                                              const RColumnRegister &colRegister, TTree *tree, RDataSource *ds);
0334 
0335 std::shared_ptr<RJittedDefine> BookDefineJit(std::string_view name, std::string_view expression, RLoopManager &lm,
0336                                              RDataSource *ds, const RColumnRegister &colRegister,
0337                                              const ColumnNames_t &branches, std::shared_ptr<RNodeBase> *prevNodeOnHeap);
0338 
0339 std::shared_ptr<RJittedDefine> BookDefinePerSampleJit(std::string_view name, std::string_view expression,
0340                                                       RLoopManager &lm, const RColumnRegister &colRegister,
0341                                                       std::shared_ptr<RNodeBase> *upcastNodeOnHeap);
0342 
0343 std::shared_ptr<RJittedVariation>
0344 BookVariationJit(const std::vector<std::string> &colNames, std::string_view variationName,
0345                  const std::vector<std::string> &variationTags, std::string_view expression, RLoopManager &lm,
0346                  RDataSource *ds, const RColumnRegister &colRegister, const ColumnNames_t &branches,
0347                  std::shared_ptr<RNodeBase> *upcastNodeOnHeap, bool isSingleColumn);
0348 
0349 std::string JitBuildAction(const ColumnNames_t &bl, std::shared_ptr<RDFDetail::RNodeBase> *prevNode,
0350                            const std::type_info &art, const std::type_info &at, void *rOnHeap, TTree *tree,
0351                            const unsigned int nSlots, const RColumnRegister &colRegister, RDataSource *ds,
0352                            std::weak_ptr<RJittedAction> *jittedActionOnHeap);
0353 
0354 // Allocate a weak_ptr on the heap, return a pointer to it. The user is responsible for deleting this weak_ptr.
0355 // This function is meant to be used by RInterface's methods that book code for jitting.
0356 // The problem it solves is that we generate code to be lazily jitted with the addresses of certain objects in them,
0357 // and we need to check those objects are still alive when the generated code is finally jitted and executed.
0358 // So we pass addresses to weak_ptrs allocated on the heap to the jitted code, which is then responsible for
0359 // the deletion of the weak_ptr object.
0360 template <typename T>
0361 std::weak_ptr<T> *MakeWeakOnHeap(const std::shared_ptr<T> &shPtr)
0362 {
0363    return new std::weak_ptr<T>(shPtr);
0364 }
0365 
0366 // Same as MakeWeakOnHeap, but create a shared_ptr that makes sure the object is definitely kept alive.
0367 template <typename T>
0368 std::shared_ptr<T> *MakeSharedOnHeap(const std::shared_ptr<T> &shPtr)
0369 {
0370    return new std::shared_ptr<T>(shPtr);
0371 }
0372 
0373 bool AtLeastOneEmptyString(const std::vector<std::string_view> strings);
0374 
0375 /// Take a shared_ptr<AnyNodeType> and return a shared_ptr<RNodeBase>.
0376 /// This works for RLoopManager nodes as well as filters and ranges.
0377 std::shared_ptr<RNodeBase> UpcastNode(std::shared_ptr<RNodeBase> ptr);
0378 
0379 ColumnNames_t GetValidatedColumnNames(RLoopManager &lm, const unsigned int nColumns, const ColumnNames_t &columns,
0380                                       const RColumnRegister &validDefines, RDataSource *ds);
0381 
0382 std::vector<std::string> GetValidatedArgTypes(const ColumnNames_t &colNames, const RColumnRegister &colRegister,
0383                                               TTree *tree, RDataSource *ds, const std::string &context,
0384                                               bool vector2rvec);
0385 
0386 std::vector<bool> FindUndefinedDSColumns(const ColumnNames_t &requestedCols, const ColumnNames_t &definedDSCols);
0387 
0388 template <typename T>
0389 void AddDSColumnsHelper(const std::string &colName, RLoopManager &lm, RDataSource &ds, RColumnRegister &colRegister)
0390 {
0391    if (colRegister.IsDefineOrAlias(colName) || !ds.HasColumn(colName) ||
0392        lm.HasDataSourceColumnReaders(colName, typeid(T)))
0393       return;
0394 
0395    const auto nSlots = lm.GetNSlots();
0396    std::vector<std::unique_ptr<RColumnReaderBase>> colReaders;
0397    colReaders.reserve(nSlots);
0398 
0399    const auto valuePtrs = ds.GetColumnReaders<T>(colName);
0400    if (!valuePtrs.empty()) { // we are using the old GetColumnReaders mechanism in this RDataSource
0401       for (auto *ptr : valuePtrs)
0402          colReaders.emplace_back(new RDSColumnReader<T>(ptr));
0403 
0404    } else { // using the new GetColumnReaders mechanism
0405       // TODO consider changing the interface so we return all of these for all slots in one go
0406       for (auto slot = 0u; slot < lm.GetNSlots(); ++slot)
0407          colReaders.emplace_back(ds.GetColumnReaders(slot, colName, typeid(T)));
0408    }
0409 
0410    lm.AddDataSourceColumnReaders(colName, std::move(colReaders), typeid(T));
0411 }
0412 
0413 /// Take list of column names that must be defined, current map of custom columns, current list of defined column names,
0414 /// and return a new map of custom columns (with the new datasource columns added to it)
0415 template <typename... ColumnTypes>
0416 void AddDSColumns(const std::vector<std::string> &requiredCols, RLoopManager &lm, RDataSource &ds,
0417                   TTraits::TypeList<ColumnTypes...>, RColumnRegister &colRegister)
0418 {
0419    // hack to expand a template parameter pack without c++17 fold expressions.
0420    using expander = int[];
0421    int i = 0;
0422    (void)expander{(AddDSColumnsHelper<ColumnTypes>(requiredCols[i], lm, ds, colRegister), ++i)..., 0};
0423 }
0424 
0425 // this function is meant to be called by the jitted code generated by BookFilterJit
0426 template <typename F, typename PrevNode>
0427 void JitFilterHelper(F &&f, const char **colsPtr, std::size_t colsSize, std::string_view name,
0428                      std::weak_ptr<RJittedFilter> *wkJittedFilter, std::shared_ptr<PrevNode> *prevNodeOnHeap,
0429                      RColumnRegister *colRegister) noexcept
0430 {
0431    if (wkJittedFilter->expired()) {
0432       // The branch of the computation graph that needed this jitted code went out of scope between the type
0433       // jitting was booked and the time jitting actually happened. Nothing to do other than cleaning up.
0434       delete wkJittedFilter;
0435       delete colRegister;
0436       delete prevNodeOnHeap;
0437       return;
0438    }
0439 
0440    const ColumnNames_t cols(colsPtr, colsPtr + colsSize);
0441    delete[] colsPtr;
0442 
0443    const auto jittedFilter = wkJittedFilter->lock();
0444 
0445    // mock Filter logic -- validity checks and Define-ition of RDataSource columns
0446    using Callable_t = std::decay_t<F>;
0447    using F_t = RFilter<Callable_t, PrevNode>;
0448    using ColTypes_t = typename TTraits::CallableTraits<Callable_t>::arg_types;
0449    constexpr auto nColumns = ColTypes_t::list_size;
0450    CheckFilter(f);
0451 
0452    auto &lm = *jittedFilter->GetLoopManagerUnchecked(); // RLoopManager must exist at this time
0453    auto ds = lm.GetDataSource();
0454 
0455    if (ds != nullptr)
0456       AddDSColumns(cols, lm, *ds, ColTypes_t(), *colRegister);
0457 
0458    jittedFilter->SetFilter(
0459       std::unique_ptr<RFilterBase>(new F_t(std::forward<F>(f), cols, *prevNodeOnHeap, *colRegister, name)));
0460    // colRegister points to the columns structure in the heap, created before the jitted call so that the jitter can
0461    // share data after it has lazily compiled the code. Here the data has been used and the memory can be freed.
0462    delete colRegister;
0463    delete prevNodeOnHeap;
0464    delete wkJittedFilter;
0465 }
0466 
0467 namespace DefineTypes {
0468 struct RDefineTag {};
0469 struct RDefinePerSampleTag {};
0470 }
0471 
0472 template <typename F>
0473 auto MakeDefineNode(DefineTypes::RDefineTag, std::string_view name, std::string_view dummyType, F &&f,
0474                     const ColumnNames_t &cols, RColumnRegister &colRegister, RLoopManager &lm)
0475 {
0476    return std::unique_ptr<RDefineBase>(new RDefine<std::decay_t<F>, ExtraArgsForDefine::None>(
0477       name, dummyType, std::forward<F>(f), cols, colRegister, lm));
0478 }
0479 
0480 template <typename F>
0481 auto MakeDefineNode(DefineTypes::RDefinePerSampleTag, std::string_view name, std::string_view dummyType, F &&f,
0482                     const ColumnNames_t &, RColumnRegister &, RLoopManager &lm)
0483 {
0484    return std::unique_ptr<RDefineBase>(
0485       new RDefinePerSample<std::decay_t<F>>(name, dummyType, std::forward<F>(f), lm));
0486 }
0487 
0488 // Build a RDefine or a RDefinePerSample object and attach it to an existing RJittedDefine
0489 // This function is meant to be called by jitted code right before starting the event loop.
0490 // If colsPtr is null, build a RDefinePerSample (it has no input columns), otherwise a RDefine.
0491 template <typename RDefineTypeTag, typename F>
0492 void JitDefineHelper(F &&f, const char **colsPtr, std::size_t colsSize, std::string_view name, RLoopManager *lm,
0493                      std::weak_ptr<RJittedDefine> *wkJittedDefine, RColumnRegister *colRegister,
0494                      std::shared_ptr<RNodeBase> *prevNodeOnHeap) noexcept
0495 {
0496    // a helper to delete objects allocated before jitting, so that the jitter can share data with lazily jitted code
0497    auto doDeletes = [&] {
0498       delete wkJittedDefine;
0499       delete colRegister;
0500       delete prevNodeOnHeap;
0501       delete[] colsPtr;
0502    };
0503 
0504    if (wkJittedDefine->expired()) {
0505       // The branch of the computation graph that needed this jitted code went out of scope between the type
0506       // jitting was booked and the time jitting actually happened. Nothing to do other than cleaning up.
0507       doDeletes();
0508       return;
0509    }
0510 
0511    const ColumnNames_t cols(colsPtr, colsPtr + colsSize);
0512 
0513    auto jittedDefine = wkJittedDefine->lock();
0514 
0515    using Callable_t = std::decay_t<F>;
0516    using ColTypes_t = typename TTraits::CallableTraits<Callable_t>::arg_types;
0517 
0518    auto ds = lm->GetDataSource();
0519    if (ds != nullptr)
0520       AddDSColumns(cols, *lm, *ds, ColTypes_t(), *colRegister);
0521 
0522    // will never actually be used (trumped by jittedDefine->GetTypeName()), but we set it to something meaningful
0523    // to help devs debugging
0524    const auto dummyType = "jittedCol_t";
0525    // use unique_ptr<RDefineBase> instead of make_unique<NewCol_t> to reduce jit/compile-times
0526    std::unique_ptr<RDefineBase> newCol{
0527       MakeDefineNode(RDefineTypeTag{}, name, dummyType, std::forward<F>(f), cols, *colRegister, *lm)};
0528    jittedDefine->SetDefine(std::move(newCol));
0529 
0530    doDeletes();
0531 }
0532 
0533 template <bool IsSingleColumn, typename F>
0534 void JitVariationHelper(F &&f, const char **colsPtr, std::size_t colsSize, const char **variedCols,
0535                         std::size_t variedColsSize, const char **variationTags, std::size_t variationTagsSize,
0536                         std::string_view variationName, RLoopManager *lm,
0537                         std::weak_ptr<RJittedVariation> *wkJittedVariation, RColumnRegister *colRegister,
0538                         std::shared_ptr<RNodeBase> *prevNodeOnHeap) noexcept
0539 {
0540    // a helper to delete objects allocated before jitting, so that the jitter can share data with lazily jitted code
0541    auto doDeletes = [&] {
0542       delete[] colsPtr;
0543       delete[] variedCols;
0544       delete[] variationTags;
0545 
0546       delete wkJittedVariation;
0547       delete colRegister;
0548       delete prevNodeOnHeap;
0549    };
0550 
0551    if (wkJittedVariation->expired()) {
0552       // The branch of the computation graph that needed this jitted variation went out of scope between the type
0553       // jitting was booked and the time jitting actually happened. Nothing to do other than cleaning up.
0554       doDeletes();
0555       return;
0556    }
0557 
0558    const ColumnNames_t inputColNames(colsPtr, colsPtr + colsSize);
0559    std::vector<std::string> variedColNames(variedCols, variedCols + variedColsSize);
0560    std::vector<std::string> tags(variationTags, variationTags + variationTagsSize);
0561 
0562    auto jittedVariation = wkJittedVariation->lock();
0563 
0564    using Callable_t = std::decay_t<F>;
0565    using ColTypes_t = typename TTraits::CallableTraits<Callable_t>::arg_types;
0566 
0567    auto ds = lm->GetDataSource();
0568    if (ds != nullptr)
0569       AddDSColumns(inputColNames, *lm, *ds, ColTypes_t(), *colRegister);
0570 
0571    // use unique_ptr<RDefineBase> instead of make_unique<NewCol_t> to reduce jit/compile-times
0572    std::unique_ptr<RVariationBase> newVariation{new RVariation<std::decay_t<F>, IsSingleColumn>(
0573       std::move(variedColNames), variationName, std::forward<F>(f), std::move(tags), jittedVariation->GetTypeName(),
0574       *colRegister, *lm, std::move(inputColNames))};
0575    jittedVariation->SetVariation(std::move(newVariation));
0576 
0577    doDeletes();
0578 }
0579 
0580 /// Convenience function invoked by jitted code to build action nodes at runtime
0581 template <typename ActionTag, typename... ColTypes, typename PrevNodeType, typename HelperArgType>
0582 void CallBuildAction(std::shared_ptr<PrevNodeType> *prevNodeOnHeap, const char **colsPtr, std::size_t colsSize,
0583                      const unsigned int nSlots, std::shared_ptr<HelperArgType> *helperArgOnHeap,
0584                      std::weak_ptr<RJittedAction> *wkJittedActionOnHeap, RColumnRegister *colRegister) noexcept
0585 {
0586    // a helper to delete objects allocated before jitting, so that the jitter can share data with lazily jitted code
0587    auto doDeletes = [&] {
0588       delete[] colsPtr;
0589       delete helperArgOnHeap;
0590       delete wkJittedActionOnHeap;
0591       // colRegister must be deleted before prevNodeOnHeap because their dtor needs the RLoopManager to be alive
0592       // and prevNodeOnHeap is what keeps it alive if the rest of the computation graph is already out of scope
0593       delete colRegister;
0594       delete prevNodeOnHeap;
0595    };
0596 
0597    if (wkJittedActionOnHeap->expired()) {
0598       // The branch of the computation graph that needed this jitted variation went out of scope between the type
0599       // jitting was booked and the time jitting actually happened. Nothing to do other than cleaning up.
0600       doDeletes();
0601       return;
0602    }
0603 
0604    const ColumnNames_t cols(colsPtr, colsPtr + colsSize);
0605 
0606    auto jittedActionOnHeap = wkJittedActionOnHeap->lock();
0607 
0608    // if we are here it means we are jitting, if we are jitting the loop manager must be alive
0609    auto &prevNodePtr = *prevNodeOnHeap;
0610    auto &loopManager = *prevNodePtr->GetLoopManagerUnchecked();
0611    using ColTypes_t = TypeList<ColTypes...>;
0612    constexpr auto nColumns = ColTypes_t::list_size;
0613    auto ds = loopManager.GetDataSource();
0614    if (ds != nullptr)
0615       AddDSColumns(cols, loopManager, *ds, ColTypes_t(), *colRegister);
0616 
0617    auto actionPtr = BuildAction<ColTypes...>(cols, std::move(*helperArgOnHeap), nSlots, std::move(prevNodePtr),
0618                                              ActionTag{}, *colRegister);
0619    jittedActionOnHeap->SetAction(std::move(actionPtr));
0620 
0621    doDeletes();
0622 }
0623 
0624 /// The contained `type` alias is `double` if `T == RInferredType`, `U` if `T == std::container<U>`, `T` otherwise.
0625 template <typename T, bool Container = IsDataContainer<T>::value && !std::is_same<T, std::string>::value>
0626 struct RMinReturnType {
0627    using type = T;
0628 };
0629 
0630 template <>
0631 struct RMinReturnType<RInferredType, false> {
0632    using type = double;
0633 };
0634 
0635 template <typename T>
0636 struct RMinReturnType<T, true> {
0637    using type = TTraits::TakeFirstParameter_t<T>;
0638 };
0639 
0640 // return wrapper around f that prepends an `unsigned int slot` parameter
0641 template <typename R, typename F, typename... Args>
0642 std::function<R(unsigned int, Args...)> AddSlotParameter(F &f, TypeList<Args...>)
0643 {
0644    return [f](unsigned int, Args... a) mutable -> R { return f(a...); };
0645 }
0646 
0647 template <typename ColType, typename... Rest>
0648 struct RNeedJittingHelper {
0649    static constexpr bool value = RNeedJittingHelper<Rest...>::value;
0650 };
0651 
0652 template <typename... Rest>
0653 struct RNeedJittingHelper<RInferredType, Rest...> {
0654    static constexpr bool value = true;
0655 };
0656 
0657 template <typename T>
0658 struct RNeedJittingHelper<T> {
0659    static constexpr bool value = false;
0660 };
0661 
0662 template <>
0663 struct RNeedJittingHelper<RInferredType> {
0664    static constexpr bool value = true;
0665 };
0666 
0667 template <typename ...ColTypes>
0668 struct RNeedJitting {
0669    static constexpr bool value = RNeedJittingHelper<ColTypes...>::value;
0670 };
0671 
0672 template <>
0673 struct RNeedJitting<> {
0674    static constexpr bool value = false;
0675 };
0676 
0677 ///////////////////////////////////////////////////////////////////////////////
0678 /// Check preconditions for RInterface::Aggregate:
0679 /// - the aggregator callable must have signature `U(U,T)` or `void(U&,T)`.
0680 /// - the merge callable must have signature `U(U,U)` or `void(std::vector<U>&)`
0681 template <typename R, typename Merge, typename U, typename T, typename decayedU = std::decay_t<U>,
0682           typename mergeArgsNoDecay_t = typename CallableTraits<Merge>::arg_types_nodecay,
0683           typename mergeArgs_t = typename CallableTraits<Merge>::arg_types,
0684           typename mergeRet_t = typename CallableTraits<Merge>::ret_type>
0685 void CheckAggregate(TypeList<U, T>)
0686 {
0687    constexpr bool isAggregatorOk =
0688       (std::is_same<R, decayedU>::value) || (std::is_same<R, void>::value && std::is_lvalue_reference<U>::value);
0689    static_assert(isAggregatorOk, "aggregator function must have signature `U(U,T)` or `void(U&,T)`");
0690    constexpr bool isMergeOk =
0691       (std::is_same<TypeList<decayedU, decayedU>, mergeArgs_t>::value && std::is_same<decayedU, mergeRet_t>::value) ||
0692       (std::is_same<TypeList<std::vector<decayedU> &>, mergeArgsNoDecay_t>::value &&
0693        std::is_same<void, mergeRet_t>::value);
0694    static_assert(isMergeOk, "merge function must have signature `U(U,U)` or `void(std::vector<U>&)`");
0695 }
0696 
0697 ///////////////////////////////////////////////////////////////////////////////
0698 /// This overload of CheckAggregate is called when the aggregator takes more than two arguments
0699 template <typename R, typename T>
0700 void CheckAggregate(T)
0701 {
0702    static_assert(sizeof(T) == 0, "aggregator function must take exactly two arguments");
0703 }
0704 
0705 ///////////////////////////////////////////////////////////////////////////////
0706 /// Check as many template parameters were passed as the number of column names, throw if this is not the case.
0707 void CheckTypesAndPars(unsigned int nTemplateParams, unsigned int nColumnNames);
0708 
0709 /// Return local BranchNames or default BranchNames according to which one should be used
0710 const ColumnNames_t SelectColumns(unsigned int nArgs, const ColumnNames_t &bl, const ColumnNames_t &defBl);
0711 
0712 /// Check whether column names refer to a valid branch of a TTree or have been `Define`d. Return invalid column names.
0713 ColumnNames_t FindUnknownColumns(const ColumnNames_t &requiredCols, const ColumnNames_t &datasetColumns,
0714                                  const RColumnRegister &definedCols, const ColumnNames_t &dataSourceColumns);
0715 
0716 /// Returns the list of Filters defined in the whole graph
0717 std::vector<std::string> GetFilterNames(const std::shared_ptr<RLoopManager> &loopManager);
0718 
0719 /// Returns the list of Filters defined in the branch
0720 template <typename NodeType>
0721 std::vector<std::string> GetFilterNames(const std::shared_ptr<NodeType> &node)
0722 {
0723    std::vector<std::string> filterNames;
0724    node->AddFilterName(filterNames);
0725    return filterNames;
0726 }
0727 
0728 struct ParsedTreePath {
0729    std::string fTreeName;
0730    std::string fDirName;
0731 };
0732 
0733 ParsedTreePath ParseTreePath(std::string_view fullTreeName);
0734 
0735 // Check if a condition is true for all types
0736 template <bool...>
0737 struct TBoolPack;
0738 
0739 template <bool... bs>
0740 using IsTrueForAllImpl_t = typename std::is_same<TBoolPack<bs..., true>, TBoolPack<true, bs...>>;
0741 
0742 template <bool... Conditions>
0743 struct TEvalAnd {
0744    static constexpr bool value = IsTrueForAllImpl_t<Conditions...>::value;
0745 };
0746 
0747 // Check if a class is a specialisation of stl containers templates
0748 // clang-format off
0749 
0750 template <typename>
0751 struct IsList_t : std::false_type {};
0752 
0753 template <typename T>
0754 struct IsList_t<std::list<T>> : std::true_type {};
0755 
0756 template <typename>
0757 struct IsDeque_t : std::false_type {};
0758 
0759 template <typename T>
0760 struct IsDeque_t<std::deque<T>> : std::true_type {};
0761 // clang-format on
0762 
0763 void CheckForDuplicateSnapshotColumns(const ColumnNames_t &cols);
0764 
0765 template <typename T>
0766 struct InnerValueType {
0767    using type = T; // fallback for when T is not a nested RVec
0768 };
0769 
0770 template <typename Elem>
0771 struct InnerValueType<ROOT::VecOps::RVec<ROOT::VecOps::RVec<Elem>>> {
0772    using type = Elem;
0773 };
0774 
0775 template <typename T>
0776 using InnerValueType_t = typename InnerValueType<T>::type;
0777 
0778 std::pair<std::vector<std::string>, std::vector<std::string>>
0779 AddSizeBranches(const std::vector<std::string> &branches, TTree *tree, std::vector<std::string> &&colsWithoutAliases,
0780                 std::vector<std::string> &&colsWithAliases);
0781 
0782 void RemoveDuplicates(ColumnNames_t &columnNames);
0783 
0784 } // namespace RDF
0785 } // namespace Internal
0786 
0787 namespace Detail {
0788 namespace RDF {
0789 
0790 /// The aliased type is `double` if `T == RInferredType`, `U` if `T == container<U>`, `T` otherwise.
0791 template <typename T>
0792 using MinReturnType_t = typename RDFInternal::RMinReturnType<T>::type;
0793 
0794 template <typename T>
0795 using MaxReturnType_t = MinReturnType_t<T>;
0796 
0797 template <typename T>
0798 using SumReturnType_t = MinReturnType_t<T>;
0799 
0800 } // namespace RDF
0801 } // namespace Detail
0802 } // namespace ROOT
0803 
0804 /// \endcond
0805 
0806 #endif