Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 09:11:32

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 <string_view>
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    ROOT::Detail::RDF::RLoopManager *fOutputLoopManager;
0254    ROOT::Detail::RDF::RLoopManager *fInputLoopManager;
0255    bool fToNTuple;
0256 };
0257 
0258 // SnapshotTTree action
0259 template <typename... ColTypes, typename PrevNodeType>
0260 std::unique_ptr<RActionBase>
0261 BuildAction(const ColumnNames_t &colNames, const std::shared_ptr<SnapshotHelperArgs> &snapHelperArgs,
0262             const unsigned int nSlots, std::shared_ptr<PrevNodeType> prevNode, ActionTags::Snapshot,
0263             const RColumnRegister &colRegister)
0264 {
0265    const auto &filename = snapHelperArgs->fFileName;
0266    const auto &dirname = snapHelperArgs->fDirName;
0267    const auto &treename = snapHelperArgs->fTreeName;
0268    const auto &outputColNames = snapHelperArgs->fOutputColNames;
0269    const auto &options = snapHelperArgs->fOptions;
0270    const auto &lmPtr = snapHelperArgs->fOutputLoopManager;
0271    const auto &inputLM = snapHelperArgs->fInputLoopManager;
0272 
0273    auto sz = sizeof...(ColTypes);
0274    std::vector<bool> isDefine(sz);
0275    for (auto i = 0u; i < sz; ++i)
0276       isDefine[i] = colRegister.IsDefineOrAlias(colNames[i]);
0277 
0278    std::unique_ptr<RActionBase> actionPtr;
0279    if (snapHelperArgs->fToNTuple) {
0280       if (!ROOT::IsImplicitMTEnabled()) {
0281          // single-thread snapshot
0282          using Helper_t = SnapshotRNTupleHelper<ColTypes...>;
0283          using Action_t = RAction<Helper_t, PrevNodeType>;
0284 
0285          actionPtr.reset(new Action_t(
0286             Helper_t(filename, dirname, treename, colNames, outputColNames, options, lmPtr, std::move(isDefine)),
0287             colNames, prevNode, colRegister));
0288       } else {
0289          // multi-thread snapshot to RNTuple is not yet supported
0290          // TODO(fdegeus) Add MT snapshotting
0291          throw std::runtime_error("Snapshot: Snapshotting to RNTuple with IMT enabled is not supported yet.");
0292       }
0293 
0294       return actionPtr;
0295    } else {
0296       if (!ROOT::IsImplicitMTEnabled()) {
0297          // single-thread snapshot
0298          using Helper_t = SnapshotTTreeHelper<ColTypes...>;
0299          using Action_t = RAction<Helper_t, PrevNodeType>;
0300          actionPtr.reset(new Action_t(Helper_t(filename, dirname, treename, colNames, outputColNames, options,
0301                                                std::move(isDefine), lmPtr, inputLM),
0302                                       colNames, prevNode, colRegister));
0303       } else {
0304          // multi-thread snapshot
0305          using Helper_t = SnapshotTTreeHelperMT<ColTypes...>;
0306          using Action_t = RAction<Helper_t, PrevNodeType>;
0307          actionPtr.reset(new Action_t(Helper_t(nSlots, filename, dirname, treename, colNames, outputColNames, options,
0308                                                std::move(isDefine), lmPtr, inputLM),
0309                                       colNames, prevNode, colRegister));
0310       }
0311    }
0312    return actionPtr;
0313 }
0314 
0315 // Book with custom helper type
0316 template <typename... ColTypes, typename PrevNodeType, typename Helper_t>
0317 std::unique_ptr<RActionBase>
0318 BuildAction(const ColumnNames_t &bl, const std::shared_ptr<Helper_t> &h, const unsigned int /*nSlots*/,
0319             std::shared_ptr<PrevNodeType> prevNode, ActionTags::Book, const RColumnRegister &colRegister)
0320 {
0321    using Action_t = RAction<Helper_t, PrevNodeType, TTraits::TypeList<ColTypes...>>;
0322    return std::make_unique<Action_t>(Helper_t(std::move(*h)), bl, std::move(prevNode), colRegister);
0323 }
0324 
0325 /****** end BuildAndBook ******/
0326 
0327 template <typename Filter>
0328 void CheckFilter(Filter &)
0329 {
0330    using FilterRet_t = typename RDF::CallableTraits<Filter>::ret_type;
0331    static_assert(std::is_convertible<FilterRet_t, bool>::value,
0332                  "filter expression returns a type that is not convertible to bool");
0333 }
0334 
0335 ColumnNames_t FilterArraySizeColNames(const ColumnNames_t &columnNames, const std::string &action);
0336 
0337 void CheckValidCppVarName(std::string_view var, const std::string &where);
0338 
0339 void CheckForRedefinition(const std::string &where, std::string_view definedCol, const RColumnRegister &colRegister,
0340                           const ColumnNames_t &treeColumns, const ColumnNames_t &dataSourceColumns);
0341 
0342 void CheckForDefinition(const std::string &where, std::string_view definedColView, const RColumnRegister &colRegister,
0343                         const ColumnNames_t &treeColumns, const ColumnNames_t &dataSourceColumns);
0344 
0345 void CheckForNoVariations(const std::string &where, std::string_view definedColView,
0346                           const RColumnRegister &colRegister);
0347 
0348 std::string PrettyPrintAddr(const void *const addr);
0349 
0350 std::shared_ptr<RJittedFilter> BookFilterJit(std::shared_ptr<RNodeBase> *prevNodeOnHeap, std::string_view name,
0351                                              std::string_view expression, const ColumnNames_t &branches,
0352                                              const RColumnRegister &colRegister, TTree *tree, RDataSource *ds);
0353 
0354 std::shared_ptr<RJittedDefine> BookDefineJit(std::string_view name, std::string_view expression, RLoopManager &lm,
0355                                              RDataSource *ds, const RColumnRegister &colRegister,
0356                                              const ColumnNames_t &branches, std::shared_ptr<RNodeBase> *prevNodeOnHeap);
0357 
0358 std::shared_ptr<RJittedDefine> BookDefinePerSampleJit(std::string_view name, std::string_view expression,
0359                                                       RLoopManager &lm, const RColumnRegister &colRegister,
0360                                                       std::shared_ptr<RNodeBase> *upcastNodeOnHeap);
0361 
0362 std::shared_ptr<RJittedVariation>
0363 BookVariationJit(const std::vector<std::string> &colNames, std::string_view variationName,
0364                  const std::vector<std::string> &variationTags, std::string_view expression, RLoopManager &lm,
0365                  RDataSource *ds, const RColumnRegister &colRegister, const ColumnNames_t &branches,
0366                  std::shared_ptr<RNodeBase> *upcastNodeOnHeap, bool isSingleColumn);
0367 
0368 std::string JitBuildAction(const ColumnNames_t &bl, std::shared_ptr<RDFDetail::RNodeBase> *prevNode,
0369                            const std::type_info &art, const std::type_info &at, void *rOnHeap, TTree *tree,
0370                            const unsigned int nSlots, const RColumnRegister &colRegister, RDataSource *ds,
0371                            std::weak_ptr<RJittedAction> *jittedActionOnHeap, const bool vector2RVec = true);
0372 
0373 // Allocate a weak_ptr on the heap, return a pointer to it. The user is responsible for deleting this weak_ptr.
0374 // This function is meant to be used by RInterface's methods that book code for jitting.
0375 // The problem it solves is that we generate code to be lazily jitted with the addresses of certain objects in them,
0376 // and we need to check those objects are still alive when the generated code is finally jitted and executed.
0377 // So we pass addresses to weak_ptrs allocated on the heap to the jitted code, which is then responsible for
0378 // the deletion of the weak_ptr object.
0379 template <typename T>
0380 std::weak_ptr<T> *MakeWeakOnHeap(const std::shared_ptr<T> &shPtr)
0381 {
0382    return new std::weak_ptr<T>(shPtr);
0383 }
0384 
0385 // Same as MakeWeakOnHeap, but create a shared_ptr that makes sure the object is definitely kept alive.
0386 template <typename T>
0387 std::shared_ptr<T> *MakeSharedOnHeap(const std::shared_ptr<T> &shPtr)
0388 {
0389    return new std::shared_ptr<T>(shPtr);
0390 }
0391 
0392 bool AtLeastOneEmptyString(const std::vector<std::string_view> strings);
0393 
0394 /// Take a shared_ptr<AnyNodeType> and return a shared_ptr<RNodeBase>.
0395 /// This works for RLoopManager nodes as well as filters and ranges.
0396 std::shared_ptr<RNodeBase> UpcastNode(std::shared_ptr<RNodeBase> ptr);
0397 
0398 ColumnNames_t GetValidatedColumnNames(RLoopManager &lm, const unsigned int nColumns, const ColumnNames_t &columns,
0399                                       const RColumnRegister &validDefines, RDataSource *ds);
0400 
0401 std::vector<std::string> GetValidatedArgTypes(const ColumnNames_t &colNames, const RColumnRegister &colRegister,
0402                                               TTree *tree, RDataSource *ds, const std::string &context,
0403                                               bool vector2RVec);
0404 
0405 std::vector<bool> FindUndefinedDSColumns(const ColumnNames_t &requestedCols, const ColumnNames_t &definedDSCols);
0406 
0407 template <typename T>
0408 void AddDSColumnsHelper(const std::string &colName, RLoopManager &lm, RDataSource &ds, RColumnRegister &colRegister)
0409 {
0410 
0411    if (colRegister.IsDefineOrAlias(colName))
0412       return;
0413 
0414    if (lm.HasDataSourceColumnReaders(colName, typeid(T)))
0415       return;
0416 
0417    if (!ds.HasColumn(colName) &&
0418        lm.GetSuppressErrorsForMissingBranches().find(colName) == lm.GetSuppressErrorsForMissingBranches().end())
0419       return;
0420 
0421    const auto nSlots = lm.GetNSlots();
0422    std::vector<std::unique_ptr<RColumnReaderBase>> colReaders;
0423    colReaders.reserve(nSlots);
0424 
0425    const auto valuePtrs = ds.GetColumnReaders<T>(colName);
0426    if (!valuePtrs.empty()) { // we are using the old GetColumnReaders mechanism in this RDataSource
0427       for (auto *ptr : valuePtrs)
0428          colReaders.emplace_back(new RDSColumnReader<T>(ptr));
0429 
0430    } else { // using the new GetColumnReaders mechanism
0431       // TODO consider changing the interface so we return all of these for all slots in one go
0432       for (auto slot = 0u; slot < lm.GetNSlots(); ++slot)
0433          colReaders.emplace_back(
0434             ROOT::Internal::RDF::CreateColumnReader(ds, slot, colName, typeid(T), /*treeReader*/ nullptr));
0435    }
0436 
0437    lm.AddDataSourceColumnReaders(colName, std::move(colReaders), typeid(T));
0438 }
0439 
0440 /// Take list of column names that must be defined, current map of custom columns, current list of defined column names,
0441 /// and return a new map of custom columns (with the new datasource columns added to it)
0442 template <typename... ColumnTypes>
0443 void AddDSColumns(const std::vector<std::string> &requiredCols, RLoopManager &lm, RDataSource &ds,
0444                   TTraits::TypeList<ColumnTypes...>, RColumnRegister &colRegister)
0445 {
0446    // hack to expand a template parameter pack without c++17 fold expressions.
0447    using expander = int[];
0448    int i = 0;
0449    (void)expander{(AddDSColumnsHelper<ColumnTypes>(requiredCols[i], lm, ds, colRegister), ++i)..., 0};
0450 }
0451 
0452 // this function is meant to be called by the jitted code generated by BookFilterJit
0453 template <typename F, typename PrevNode>
0454 void JitFilterHelper(F &&f, const char **colsPtr, std::size_t colsSize, std::string_view name,
0455                      std::weak_ptr<RJittedFilter> *wkJittedFilter, std::shared_ptr<PrevNode> *prevNodeOnHeap,
0456                      RColumnRegister *colRegister) noexcept
0457 {
0458    if (wkJittedFilter->expired()) {
0459       // The branch of the computation graph that needed this jitted code went out of scope between the type
0460       // jitting was booked and the time jitting actually happened. Nothing to do other than cleaning up.
0461       delete wkJittedFilter;
0462       delete colRegister;
0463       delete prevNodeOnHeap;
0464       return;
0465    }
0466 
0467    const ColumnNames_t cols(colsPtr, colsPtr + colsSize);
0468    delete[] colsPtr;
0469 
0470    const auto jittedFilter = wkJittedFilter->lock();
0471 
0472    // mock Filter logic -- validity checks and Define-ition of RDataSource columns
0473    using Callable_t = std::decay_t<F>;
0474    using F_t = RFilter<Callable_t, PrevNode>;
0475    using ColTypes_t = typename TTraits::CallableTraits<Callable_t>::arg_types;
0476    constexpr auto nColumns = ColTypes_t::list_size;
0477    CheckFilter(f);
0478 
0479    auto &lm = *jittedFilter->GetLoopManagerUnchecked(); // RLoopManager must exist at this time
0480    auto ds = lm.GetDataSource();
0481 
0482    if (ds != nullptr)
0483       AddDSColumns(cols, lm, *ds, ColTypes_t(), *colRegister);
0484 
0485    jittedFilter->SetFilter(
0486       std::unique_ptr<RFilterBase>(new F_t(std::forward<F>(f), cols, *prevNodeOnHeap, *colRegister, name)));
0487    // colRegister points to the columns structure in the heap, created before the jitted call so that the jitter can
0488    // share data after it has lazily compiled the code. Here the data has been used and the memory can be freed.
0489    delete colRegister;
0490    delete prevNodeOnHeap;
0491    delete wkJittedFilter;
0492 }
0493 
0494 namespace DefineTypes {
0495 struct RDefineTag {};
0496 struct RDefinePerSampleTag {};
0497 }
0498 
0499 template <typename F>
0500 auto MakeDefineNode(DefineTypes::RDefineTag, std::string_view name, std::string_view dummyType, F &&f,
0501                     const ColumnNames_t &cols, RColumnRegister &colRegister, RLoopManager &lm)
0502 {
0503    return std::unique_ptr<RDefineBase>(new RDefine<std::decay_t<F>, ExtraArgsForDefine::None>(
0504       name, dummyType, std::forward<F>(f), cols, colRegister, lm));
0505 }
0506 
0507 template <typename F>
0508 auto MakeDefineNode(DefineTypes::RDefinePerSampleTag, std::string_view name, std::string_view dummyType, F &&f,
0509                     const ColumnNames_t &, RColumnRegister &, RLoopManager &lm)
0510 {
0511    return std::unique_ptr<RDefineBase>(
0512       new RDefinePerSample<std::decay_t<F>>(name, dummyType, std::forward<F>(f), lm));
0513 }
0514 
0515 // Build a RDefine or a RDefinePerSample object and attach it to an existing RJittedDefine
0516 // This function is meant to be called by jitted code right before starting the event loop.
0517 // If colsPtr is null, build a RDefinePerSample (it has no input columns), otherwise a RDefine.
0518 template <typename RDefineTypeTag, typename F>
0519 void JitDefineHelper(F &&f, const char **colsPtr, std::size_t colsSize, std::string_view name, RLoopManager *lm,
0520                      std::weak_ptr<RJittedDefine> *wkJittedDefine, RColumnRegister *colRegister,
0521                      std::shared_ptr<RNodeBase> *prevNodeOnHeap) noexcept
0522 {
0523    // a helper to delete objects allocated before jitting, so that the jitter can share data with lazily jitted code
0524    auto doDeletes = [&] {
0525       delete wkJittedDefine;
0526       delete colRegister;
0527       delete prevNodeOnHeap;
0528       delete[] colsPtr;
0529    };
0530 
0531    if (wkJittedDefine->expired()) {
0532       // The branch of the computation graph that needed this jitted code went out of scope between the type
0533       // jitting was booked and the time jitting actually happened. Nothing to do other than cleaning up.
0534       doDeletes();
0535       return;
0536    }
0537 
0538    const ColumnNames_t cols(colsPtr, colsPtr + colsSize);
0539 
0540    auto jittedDefine = wkJittedDefine->lock();
0541 
0542    using Callable_t = std::decay_t<F>;
0543    using ColTypes_t = typename TTraits::CallableTraits<Callable_t>::arg_types;
0544 
0545    auto ds = lm->GetDataSource();
0546    if (ds != nullptr && colsPtr)
0547       AddDSColumns(cols, *lm, *ds, ColTypes_t(), *colRegister);
0548 
0549    // will never actually be used (trumped by jittedDefine->GetTypeName()), but we set it to something meaningful
0550    // to help devs debugging
0551    const auto dummyType = "jittedCol_t";
0552    // use unique_ptr<RDefineBase> instead of make_unique<NewCol_t> to reduce jit/compile-times
0553    std::unique_ptr<RDefineBase> newCol{
0554       MakeDefineNode(RDefineTypeTag{}, name, dummyType, std::forward<F>(f), cols, *colRegister, *lm)};
0555    jittedDefine->SetDefine(std::move(newCol));
0556 
0557    doDeletes();
0558 }
0559 
0560 template <bool IsSingleColumn, typename F>
0561 void JitVariationHelper(F &&f, const char **colsPtr, std::size_t colsSize, const char **variedCols,
0562                         std::size_t variedColsSize, const char **variationTags, std::size_t variationTagsSize,
0563                         std::string_view variationName, RLoopManager *lm,
0564                         std::weak_ptr<RJittedVariation> *wkJittedVariation, RColumnRegister *colRegister,
0565                         std::shared_ptr<RNodeBase> *prevNodeOnHeap) noexcept
0566 {
0567    // a helper to delete objects allocated before jitting, so that the jitter can share data with lazily jitted code
0568    auto doDeletes = [&] {
0569       delete[] colsPtr;
0570       delete[] variedCols;
0571       delete[] variationTags;
0572 
0573       delete wkJittedVariation;
0574       delete colRegister;
0575       delete prevNodeOnHeap;
0576    };
0577 
0578    if (wkJittedVariation->expired()) {
0579       // The branch of the computation graph that needed this jitted variation went out of scope between the type
0580       // jitting was booked and the time jitting actually happened. Nothing to do other than cleaning up.
0581       doDeletes();
0582       return;
0583    }
0584 
0585    const ColumnNames_t inputColNames(colsPtr, colsPtr + colsSize);
0586    std::vector<std::string> variedColNames(variedCols, variedCols + variedColsSize);
0587    std::vector<std::string> tags(variationTags, variationTags + variationTagsSize);
0588 
0589    auto jittedVariation = wkJittedVariation->lock();
0590 
0591    using Callable_t = std::decay_t<F>;
0592    using ColTypes_t = typename TTraits::CallableTraits<Callable_t>::arg_types;
0593 
0594    auto ds = lm->GetDataSource();
0595    if (ds != nullptr)
0596       AddDSColumns(inputColNames, *lm, *ds, ColTypes_t(), *colRegister);
0597 
0598    // use unique_ptr<RDefineBase> instead of make_unique<NewCol_t> to reduce jit/compile-times
0599    std::unique_ptr<RVariationBase> newVariation{new RVariation<std::decay_t<F>, IsSingleColumn>(
0600       std::move(variedColNames), variationName, std::forward<F>(f), std::move(tags), jittedVariation->GetTypeName(),
0601       *colRegister, *lm, inputColNames)};
0602    jittedVariation->SetVariation(std::move(newVariation));
0603 
0604    doDeletes();
0605 }
0606 
0607 /// Convenience function invoked by jitted code to build action nodes at runtime
0608 template <typename ActionTag, typename... ColTypes, typename PrevNodeType, typename HelperArgType>
0609 void CallBuildAction(std::shared_ptr<PrevNodeType> *prevNodeOnHeap, const char **colsPtr, std::size_t colsSize,
0610                      const unsigned int nSlots, std::shared_ptr<HelperArgType> *helperArgOnHeap,
0611                      std::weak_ptr<RJittedAction> *wkJittedActionOnHeap, RColumnRegister *colRegister) noexcept
0612 {
0613    // a helper to delete objects allocated before jitting, so that the jitter can share data with lazily jitted code
0614    auto doDeletes = [&] {
0615       delete[] colsPtr;
0616       delete helperArgOnHeap;
0617       delete wkJittedActionOnHeap;
0618       // colRegister must be deleted before prevNodeOnHeap because their dtor needs the RLoopManager to be alive
0619       // and prevNodeOnHeap is what keeps it alive if the rest of the computation graph is already out of scope
0620       delete colRegister;
0621       delete prevNodeOnHeap;
0622    };
0623 
0624    if (wkJittedActionOnHeap->expired()) {
0625       // The branch of the computation graph that needed this jitted variation went out of scope between the type
0626       // jitting was booked and the time jitting actually happened. Nothing to do other than cleaning up.
0627       doDeletes();
0628       return;
0629    }
0630 
0631    const ColumnNames_t cols(colsPtr, colsPtr + colsSize);
0632 
0633    auto jittedActionOnHeap = wkJittedActionOnHeap->lock();
0634 
0635    // if we are here it means we are jitting, if we are jitting the loop manager must be alive
0636    auto &prevNodePtr = *prevNodeOnHeap;
0637    auto &loopManager = *prevNodePtr->GetLoopManagerUnchecked();
0638    using ColTypes_t = TypeList<ColTypes...>;
0639    constexpr auto nColumns = ColTypes_t::list_size;
0640    auto ds = loopManager.GetDataSource();
0641    if (ds != nullptr)
0642       AddDSColumns(cols, loopManager, *ds, ColTypes_t(), *colRegister);
0643 
0644    auto actionPtr = BuildAction<ColTypes...>(cols, std::move(*helperArgOnHeap), nSlots, std::move(prevNodePtr),
0645                                              ActionTag{}, *colRegister);
0646    jittedActionOnHeap->SetAction(std::move(actionPtr));
0647 
0648    doDeletes();
0649 }
0650 
0651 /// The contained `type` alias is `double` if `T == RInferredType`, `U` if `T == std::container<U>`, `T` otherwise.
0652 template <typename T, bool Container = IsDataContainer<T>::value && !std::is_same<T, std::string>::value>
0653 struct RMinReturnType {
0654    using type = T;
0655 };
0656 
0657 template <>
0658 struct RMinReturnType<RInferredType, false> {
0659    using type = double;
0660 };
0661 
0662 template <typename T>
0663 struct RMinReturnType<T, true> {
0664    using type = TTraits::TakeFirstParameter_t<T>;
0665 };
0666 
0667 // return wrapper around f that prepends an `unsigned int slot` parameter
0668 template <typename R, typename F, typename... Args>
0669 std::function<R(unsigned int, Args...)> AddSlotParameter(F &f, TypeList<Args...>)
0670 {
0671    return [f](unsigned int, Args... a) mutable -> R { return f(a...); };
0672 }
0673 
0674 template <typename ColType, typename... Rest>
0675 struct RNeedJittingHelper {
0676    static constexpr bool value = RNeedJittingHelper<Rest...>::value;
0677 };
0678 
0679 template <typename... Rest>
0680 struct RNeedJittingHelper<RInferredType, Rest...> {
0681    static constexpr bool value = true;
0682 };
0683 
0684 template <typename T>
0685 struct RNeedJittingHelper<T> {
0686    static constexpr bool value = false;
0687 };
0688 
0689 template <>
0690 struct RNeedJittingHelper<RInferredType> {
0691    static constexpr bool value = true;
0692 };
0693 
0694 template <typename ...ColTypes>
0695 struct RNeedJitting {
0696    static constexpr bool value = RNeedJittingHelper<ColTypes...>::value;
0697 };
0698 
0699 template <>
0700 struct RNeedJitting<> {
0701    static constexpr bool value = false;
0702 };
0703 
0704 ///////////////////////////////////////////////////////////////////////////////
0705 /// Check preconditions for RInterface::Aggregate:
0706 /// - the aggregator callable must have signature `U(U,T)` or `void(U&,T)`.
0707 /// - the merge callable must have signature `U(U,U)` or `void(std::vector<U>&)`
0708 template <typename R, typename Merge, typename U, typename T, typename decayedU = std::decay_t<U>,
0709           typename mergeArgsNoDecay_t = typename CallableTraits<Merge>::arg_types_nodecay,
0710           typename mergeArgs_t = typename CallableTraits<Merge>::arg_types,
0711           typename mergeRet_t = typename CallableTraits<Merge>::ret_type>
0712 void CheckAggregate(TypeList<U, T>)
0713 {
0714    constexpr bool isAggregatorOk =
0715       (std::is_same<R, decayedU>::value) || (std::is_same<R, void>::value && std::is_lvalue_reference<U>::value);
0716    static_assert(isAggregatorOk, "aggregator function must have signature `U(U,T)` or `void(U&,T)`");
0717    constexpr bool isMergeOk =
0718       (std::is_same<TypeList<decayedU, decayedU>, mergeArgs_t>::value && std::is_same<decayedU, mergeRet_t>::value) ||
0719       (std::is_same<TypeList<std::vector<decayedU> &>, mergeArgsNoDecay_t>::value &&
0720        std::is_same<void, mergeRet_t>::value);
0721    static_assert(isMergeOk, "merge function must have signature `U(U,U)` or `void(std::vector<U>&)`");
0722 }
0723 
0724 ///////////////////////////////////////////////////////////////////////////////
0725 /// This overload of CheckAggregate is called when the aggregator takes more than two arguments
0726 template <typename R, typename T>
0727 void CheckAggregate(T)
0728 {
0729    static_assert(sizeof(T) == 0, "aggregator function must take exactly two arguments");
0730 }
0731 
0732 ///////////////////////////////////////////////////////////////////////////////
0733 /// Check as many template parameters were passed as the number of column names, throw if this is not the case.
0734 void CheckTypesAndPars(unsigned int nTemplateParams, unsigned int nColumnNames);
0735 
0736 /// Return local BranchNames or default BranchNames according to which one should be used
0737 const ColumnNames_t SelectColumns(unsigned int nArgs, const ColumnNames_t &bl, const ColumnNames_t &defBl);
0738 
0739 /// Check whether column names refer to a valid branch of a TTree or have been `Define`d. Return invalid column names.
0740 ColumnNames_t FindUnknownColumns(const ColumnNames_t &requiredCols, const ColumnNames_t &datasetColumns,
0741                                  const RColumnRegister &definedCols, const ColumnNames_t &dataSourceColumns);
0742 
0743 /// Returns the list of Filters defined in the whole graph
0744 std::vector<std::string> GetFilterNames(const std::shared_ptr<RLoopManager> &loopManager);
0745 
0746 /// Returns the list of Filters defined in the branch
0747 template <typename NodeType>
0748 std::vector<std::string> GetFilterNames(const std::shared_ptr<NodeType> &node)
0749 {
0750    std::vector<std::string> filterNames;
0751    node->AddFilterName(filterNames);
0752    return filterNames;
0753 }
0754 
0755 struct ParsedTreePath {
0756    std::string fTreeName;
0757    std::string fDirName;
0758 };
0759 
0760 ParsedTreePath ParseTreePath(std::string_view fullTreeName);
0761 
0762 // Check if a condition is true for all types
0763 template <bool...>
0764 struct TBoolPack;
0765 
0766 template <bool... bs>
0767 using IsTrueForAllImpl_t = typename std::is_same<TBoolPack<bs..., true>, TBoolPack<true, bs...>>;
0768 
0769 template <bool... Conditions>
0770 struct TEvalAnd {
0771    static constexpr bool value = IsTrueForAllImpl_t<Conditions...>::value;
0772 };
0773 
0774 // Check if a class is a specialisation of stl containers templates
0775 // clang-format off
0776 
0777 template <typename>
0778 struct IsList_t : std::false_type {};
0779 
0780 template <typename T>
0781 struct IsList_t<std::list<T>> : std::true_type {};
0782 
0783 template <typename>
0784 struct IsDeque_t : std::false_type {};
0785 
0786 template <typename T>
0787 struct IsDeque_t<std::deque<T>> : std::true_type {};
0788 // clang-format on
0789 
0790 void CheckForDuplicateSnapshotColumns(const ColumnNames_t &cols);
0791 
0792 template <typename T>
0793 struct InnerValueType {
0794    using type = T; // fallback for when T is not a nested RVec
0795 };
0796 
0797 template <typename Elem>
0798 struct InnerValueType<ROOT::VecOps::RVec<ROOT::VecOps::RVec<Elem>>> {
0799    using type = Elem;
0800 };
0801 
0802 template <typename T>
0803 using InnerValueType_t = typename InnerValueType<T>::type;
0804 
0805 std::pair<std::vector<std::string>, std::vector<std::string>>
0806 AddSizeBranches(const std::vector<std::string> &branches, ROOT::RDF::RDataSource *ds,
0807                 std::vector<std::string> &&colsWithoutAliases, std::vector<std::string> &&colsWithAliases);
0808 
0809 void RemoveDuplicates(ColumnNames_t &columnNames);
0810 void RemoveRNTupleSubFields(ColumnNames_t &columnNames);
0811 
0812 } // namespace RDF
0813 } // namespace Internal
0814 
0815 namespace Detail {
0816 namespace RDF {
0817 
0818 /// The aliased type is `double` if `T == RInferredType`, `U` if `T == container<U>`, `T` otherwise.
0819 template <typename T>
0820 using MinReturnType_t = typename RDFInternal::RMinReturnType<T>::type;
0821 
0822 template <typename T>
0823 using MaxReturnType_t = MinReturnType_t<T>;
0824 
0825 template <typename T>
0826 using SumReturnType_t = MinReturnType_t<T>;
0827 
0828 } // namespace RDF
0829 } // namespace Detail
0830 } // namespace ROOT
0831 
0832 /// \endcond
0833 
0834 #endif