Logo ROOT  
Reference Guide
RResultPtr.hxx
Go to the documentation of this file.
1 // Author: Enrico Guiraud, Danilo Piparo CERN 03/2017
2 
3 /*************************************************************************
4  * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers. *
5  * All rights reserved. *
6  * *
7  * For the licensing terms see $ROOTSYS/LICENSE. *
8  * For the list of contributors see $ROOTSYS/README/CREDITS. *
9  *************************************************************************/
10 
11 #ifndef ROOT_RRESULTPTR
12 #define ROOT_RRESULTPTR
13 
14 #include "ROOT/RDF/RActionBase.hxx"
15 #include "RtypesCore.h"
17 #include "ROOT/TypeTraits.hxx"
18 #include "TError.h" // Warning
19 
20 #include <memory>
21 #include <functional>
22 
23 namespace ROOT {
24 namespace Internal {
25 namespace RDF {
26 class GraphCreatorHelper;
27 }
28 } // namespace Internal
29 } // namespace ROOT
30 
31 namespace ROOT {
32 namespace RDF {
33 // Fwd decl for MakeResultPtr
34 template <typename T>
35 class RResultPtr;
36 } // namespace RDF
37 
38 namespace Detail {
39 namespace RDF {
41 // Fwd decl for RResultPtr
42 template <typename T>
43 RResultPtr<T> MakeResultPtr(const std::shared_ptr<T> &r, RLoopManager &df,
44  std::shared_ptr<ROOT::Internal::RDF::RActionBase> actionPtr);
45 
46 // Fwd decl for GetMergeableValue
47 template <typename T>
48 class RMergeableValue;
49 
50 template <typename T>
51 std::unique_ptr<RMergeableValue<T>> GetMergeableValue(RResultPtr<T> &rptr);
52 } // namespace RDF
53 } // namespace Detail
54 namespace RDF {
56 namespace RDFDetail = ROOT::Detail::RDF;
57 namespace TTraits = ROOT::TypeTraits;
58 
59 /// Smart pointer for the return type of actions
60 /**
61 \class ROOT::RDF::RResultPtr
62 \ingroup dataframe
63 \brief A wrapper around the result of RDataFrame actions able to trigger calculations lazily.
64 \tparam T Type of the action result
65 
66 A smart pointer which allows to access the result of a RDataFrame action. The
67 methods of the encapsulated object can be accessed via the arrow operator.
68 Upon invocation of the arrow operator or dereferencing (`operator*`), the
69 loop on the events and calculations of all scheduled actions are executed
70 if needed.
71 It is possible to iterate on the result proxy if the proxied object is a collection.
72 ~~~{.cpp}
73 for (auto& myItem : myResultProxy) { ... };
74 ~~~
75 If iteration is not supported by the type of the proxied object, a compilation error is thrown.
76 
77 */
78 template <typename T>
79 class RResultPtr {
80  // private using declarations
81  using SPT_t = std::shared_ptr<T>;
82 
83  // friend declarations
84  template <typename T1>
85  friend class RResultPtr;
86 
87  template <typename T1>
89  std::shared_ptr<RDFInternal::RActionBase>);
90  template <class T1, class T2>
91  friend bool operator==(const RResultPtr<T1> &lhs, const RResultPtr<T2> &rhs);
92  template <class T1, class T2>
93  friend bool operator!=(const RResultPtr<T1> &lhs, const RResultPtr<T2> &rhs);
94  template <class T1>
95  friend bool operator==(const RResultPtr<T1> &lhs, std::nullptr_t rhs);
96  template <class T1>
97  friend bool operator==(std::nullptr_t lhs, const RResultPtr<T1> &rhs);
98  template <class T1>
99  friend bool operator!=(const RResultPtr<T1> &lhs, std::nullptr_t rhs);
100  template <class T1>
101  friend bool operator!=(std::nullptr_t lhs, const RResultPtr<T1> &rhs);
102  friend std::unique_ptr<RDFDetail::RMergeableValue<T>> RDFDetail::GetMergeableValue<T>(RResultPtr<T> &rptr);
103 
105 
106  friend class RResultHandle;
107 
108  /// \cond HIDDEN_SYMBOLS
109  template <typename V, bool hasBeginEnd = TTraits::HasBeginAndEnd<V>::value>
110  struct RIterationHelper {
111  using Iterator_t = void;
112  void GetBegin(const V &) { static_assert(sizeof(V) == 0, "It does not make sense to ask begin for this class."); }
113  void GetEnd(const V &) { static_assert(sizeof(V) == 0, "It does not make sense to ask end for this class."); }
114  };
115 
116  template <typename V>
117  struct RIterationHelper<V, true> {
118  using Iterator_t = decltype(std::begin(std::declval<V>()));
119  static Iterator_t GetBegin(const V &v) { return std::begin(v); };
120  static Iterator_t GetEnd(const V &v) { return std::end(v); };
121  };
122  /// \endcond
123 
124  /// Non-owning pointer to the RLoopManager at the root of this computation graph.
125  /// The RLoopManager is guaranteed to be always in scope if fLoopManager is not a nullptr.
127  SPT_t fObjPtr; ///< Shared pointer encapsulating the wrapped result
128  /// Owning pointer to the action that will produce this result.
129  /// Ownership is shared with other copies of this ResultPtr.
130  std::shared_ptr<RDFInternal::RActionBase> fActionPtr;
131 
132  /// Triggers the event loop in the RLoopManager
133  void TriggerRun();
134 
135  /// Get the pointer to the encapsulated result.
136  /// Ownership is not transferred to the caller.
137  /// Triggers event loop and execution of all actions booked in the associated RLoopManager.
138  T *Get()
139  {
140  if (!fActionPtr->HasRun())
141  TriggerRun();
142  return fObjPtr.get();
143  }
144 
145  RResultPtr(std::shared_ptr<T> objPtr, RDFDetail::RLoopManager *lm,
146  std::shared_ptr<RDFInternal::RActionBase> actionPtr)
147  : fLoopManager(lm), fObjPtr(std::move(objPtr)), fActionPtr(std::move(actionPtr))
148  {
149  }
150 
151 public:
152  using Value_t = T; ///< Convenience alias to simplify access to proxied type
153  static constexpr ULong64_t kOnce = 0ull; ///< Convenience definition to express a callback must be executed once
154 
155  RResultPtr() = default;
156  RResultPtr(const RResultPtr &) = default;
157  RResultPtr(RResultPtr &&) = default;
158  RResultPtr &operator=(const RResultPtr &) = default;
160  explicit operator bool() const { return bool(fObjPtr); }
161  template <typename TO, typename std::enable_if<std::is_convertible<T, TO>::value, int>::type = 0>
162  operator RResultPtr<TO>() const
163  {
164  RResultPtr<TO> rp;
166  rp.fObjPtr = fObjPtr;
167  rp.fActionPtr = fActionPtr;
168  return rp;
169  }
170 
171  /// Get a const reference to the encapsulated object.
172  /// Triggers event loop and execution of all actions booked in the associated RLoopManager.
173  const T &GetValue() { return *Get(); }
174 
175  /// Get the pointer to the encapsulated object.
176  /// Triggers event loop and execution of all actions booked in the associated RLoopManager.
177  T *GetPtr() { return Get(); }
178 
179  /// Get a pointer to the encapsulated object.
180  /// Triggers event loop and execution of all actions booked in the associated RLoopManager.
181  T &operator*() { return *Get(); }
182 
183  /// Get a pointer to the encapsulated object.
184  /// Ownership is not transferred to the caller.
185  /// Triggers event loop and execution of all actions booked in the associated RLoopManager.
186  T *operator->() { return Get(); }
187 
188  /// Return an iterator to the beginning of the contained object if this makes
189  /// sense, throw a compilation error otherwise
190  typename RIterationHelper<T>::Iterator_t begin()
191  {
192  if (!fActionPtr->HasRun())
193  TriggerRun();
194  return RIterationHelper<T>::GetBegin(*fObjPtr);
195  }
196 
197  /// Return an iterator to the end of the contained object if this makes
198  /// sense, throw a compilation error otherwise
199  typename RIterationHelper<T>::Iterator_t end()
200  {
201  if (!fActionPtr->HasRun())
202  TriggerRun();
203  return RIterationHelper<T>::GetEnd(*fObjPtr);
204  }
205 
206  // clang-format off
207  /// Register a callback that RDataFrame will execute "everyNEvents" on a partial result.
208  ///
209  /// \param[in] everyNEvents Frequency at which the callback will be called, as a number of events processed
210  /// \param[in] callback a callable with signature `void(Value_t&)` where Value_t is the type of the value contained in this RResultPtr
211  /// \return this RResultPtr, to allow chaining of OnPartialResultSlot with other calls
212  ///
213  /// The callback must be a callable (lambda, function, functor class...) that takes a reference to the result type as
214  /// argument and returns nothing. RDataFrame will invoke registered callbacks passing partial action results as
215  /// arguments to them (e.g. a histogram filled with a part of the selected events, a counter incremented only up to a
216  /// certain point, a mean over a subset of the events and so forth).
217  ///
218  /// Callbacks can be used e.g. to inspect partial results of the analysis while the event loop is running. For
219  /// example one can draw an up-to-date version of a result histogram every 100 entries like this:
220  /// \code{.cpp}
221  /// auto h = tdf.Histo1D("x");
222  /// TCanvas c("c","x hist");
223  /// h.OnPartialResult(100, [&c](TH1D &h_) { c.cd(); h_.Draw(); c.Update(); });
224  /// h->Draw(); // event loop runs here, this `Draw` is executed after the event loop is finished
225  /// \endcode
226  ///
227  /// A value of 0 for everyNEvents indicates the callback must be executed only once, before running the event loop.
228  /// A conveniece definition `kOnce` is provided to make this fact more expressive in user code (see snippet below).
229  /// Multiple callbacks can be registered with the same RResultPtr (i.e. results of RDataFrame actions) and will
230  /// be executed sequentially. Callbacks are executed in the order they were registered.
231  /// The type of the value contained in a RResultPtr is also available as RResultPtr<T>::Value_t, e.g.
232  /// \code{.cpp}
233  /// auto h = tdf.Histo1D("x");
234  /// // h.kOnce is 0
235  /// // decltype(h)::Value_t is TH1D
236  /// \endcode
237  ///
238  /// When implicit multi-threading is enabled, the callback:
239  /// - will never be executed by multiple threads concurrently: it needs not be thread-safe. For example the snippet
240  /// above that draws the partial histogram on a canvas works seamlessly in multi-thread event loops.
241  /// - will always be executed "everyNEvents": partial results will "contain" that number of events more from
242  /// one call to the next
243  /// - might be executed by a different worker thread at different times: the value of `std::this_thread::get_id()`
244  /// might change between calls
245  ///
246  /// To register a callback that is called by _each_ worker thread (concurrently) every N events one can use
247  /// OnPartialResultSlot().
248  // clang-format on
249  RResultPtr<T> &OnPartialResult(ULong64_t everyNEvents, std::function<void(T &)> callback)
250  {
251  const auto nSlots = fLoopManager->GetNSlots();
252  auto actionPtr = fActionPtr;
253  auto c = [nSlots, actionPtr, callback](unsigned int slot) {
254  if (slot != nSlots - 1)
255  return;
256  auto partialResult = static_cast<Value_t *>(actionPtr->PartialUpdate(slot));
257  callback(*partialResult);
258  };
259  fLoopManager->RegisterCallback(everyNEvents, std::move(c));
260  return *this;
261  }
262 
263  // clang-format off
264  /// Register a callback that RDataFrame will execute in each worker thread concurrently on that thread's partial result.
265  ///
266  /// \param[in] everyNEvents Frequency at which the callback will be called by each thread, as a number of events processed
267  /// \param[in] callback A callable with signature `void(unsigned int, Value_t&)` where Value_t is the type of the value contained in this RResultPtr
268  /// \return this RResultPtr, to allow chaining of OnPartialResultSlot with other calls
269  ///
270  /// See `OnPartialResult` for a generic explanation of the callback mechanism.
271  /// Compared to `OnPartialResult`, this method has two major differences:
272  /// - all worker threads invoke the callback once every specified number of events. The event count is per-thread,
273  /// and callback invocation might happen concurrently (i.e. the callback must be thread-safe)
274  /// - the callable must take an extra `unsigned int` parameter corresponding to a multi-thread "processing slot":
275  /// this is a "helper value" to simplify writing thread-safe callbacks: different worker threads might invoke the
276  /// callback concurrently but always with different `slot` numbers.
277  /// - a value of 0 for everyNEvents indicates the callback must be executed once _per slot_.
278  ///
279  /// For example, the following snippet prints out a thread-safe progress bar of the events processed by RDataFrame
280  /// \code
281  /// auto c = tdf.Count(); // any action would do, but `Count` is the most lightweight
282  /// std::string progress;
283  /// std::mutex bar_mutex;
284  /// c.OnPartialResultSlot(nEvents / 100, [&progress, &bar_mutex](unsigned int, ULong64_t &) {
285  /// std::lock_guard<std::mutex> lg(bar_mutex);
286  /// progress.push_back('#');
287  /// std::cout << "\r[" << std::left << std::setw(100) << progress << ']' << std::flush;
288  /// });
289  /// std::cout << "Analysis running..." << std::endl;
290  /// *c; // trigger the event loop by accessing an action's result
291  /// std::cout << "\nDone!" << std::endl;
292  /// \endcode
293  // clang-format on
294  RResultPtr<T> &OnPartialResultSlot(ULong64_t everyNEvents, std::function<void(unsigned int, T &)> callback)
295  {
296  auto actionPtr = fActionPtr;
297  auto c = [actionPtr, callback](unsigned int slot) {
298  auto partialResult = static_cast<Value_t *>(actionPtr->PartialUpdate(slot));
299  callback(slot, *partialResult);
300  };
301  fLoopManager->RegisterCallback(everyNEvents, std::move(c));
302  return *this;
303  }
304 
305  // clang-format off
306  /// Check whether the result has already been computed
307  ///
308  /// ~~~{.cpp}
309  /// auto res = df.Count();
310  /// res.IsReady(); // false, access will trigger event loop
311  /// std::cout << *res << std::endl; // triggers event loop
312  /// res.IsReady(); // true
313  /// ~~~
314  // clang-format on
315  bool IsReady() const
316  {
317  return fActionPtr->HasRun();
318  }
319 };
320 
321 template <typename T>
323 {
324  fLoopManager->Run();
325 }
326 
327 template <class T1, class T2>
328 bool operator==(const RResultPtr<T1> &lhs, const RResultPtr<T2> &rhs)
329 {
330  return lhs.fObjPtr == rhs.fObjPtr;
331 }
332 
333 template <class T1, class T2>
334 bool operator!=(const RResultPtr<T1> &lhs, const RResultPtr<T2> &rhs)
335 {
336  return lhs.fObjPtr != rhs.fObjPtr;
337 }
338 
339 template <class T1>
340 bool operator==(const RResultPtr<T1> &lhs, std::nullptr_t rhs)
341 {
342  return lhs.fObjPtr == rhs;
343 }
344 
345 template <class T1>
346 bool operator==(std::nullptr_t lhs, const RResultPtr<T1> &rhs)
347 {
348  return lhs == rhs.fObjPtr;
349 }
350 
351 template <class T1>
352 bool operator!=(const RResultPtr<T1> &lhs, std::nullptr_t rhs)
353 {
354  return lhs.fObjPtr != rhs;
355 }
356 
357 template <class T1>
358 bool operator!=(std::nullptr_t lhs, const RResultPtr<T1> &rhs)
359 {
360  return lhs != rhs.fObjPtr;
361 }
362 
363 } // namespace RDF
364 
365 namespace Detail {
366 namespace RDF {
367 /// Create a RResultPtr and set its pointer to the corresponding RAction
368 /// This overload is invoked by non-jitted actions, as they have access to RAction before constructing RResultPtr.
369 template <typename T>
370 RResultPtr<T>
371 MakeResultPtr(const std::shared_ptr<T> &r, RLoopManager &lm, std::shared_ptr<RDFInternal::RActionBase> actionPtr)
372 {
373  return RResultPtr<T>(r, &lm, std::move(actionPtr));
374 }
375 
376 ////////////////////////////////////////////////////////////////////////////////
377 /// \brief Retrieve a mergeable value from an RDataFrame action.
378 /// \param[in] rptr lvalue reference of an RResultPtr object.
379 /// \returns An RMergeableValue holding the result of the action, wrapped in an
380 /// `std::unique_ptr`.
381 ///
382 /// This function triggers the execution of the RDataFrame computation graph.
383 /// Then retrieves an RMergeableValue object created with the result wrapped by
384 /// the RResultPtr argument. The user obtains ownership of the mergeable, which
385 /// in turn holds a copy of the result of the action. The RResultPtr is not
386 /// destroyed in the process and will still retain (shared) ownership of the
387 /// original result.
388 ///
389 /// Example usage:
390 /// ~~~{.cpp}
391 /// using namespace ROOT::Detail::RDF;
392 /// ROOT::RDataFrame d("myTree", "file_*.root");
393 /// auto h = d.Histo1D("Branch_A");
394 /// auto mergeablehisto = GetMergeableValue(h);
395 /// ~~~
396 template <typename T>
397 std::unique_ptr<RMergeableValue<T>> GetMergeableValue(RResultPtr<T> &rptr)
398 {
399 
400  if (!rptr.fActionPtr->HasRun())
401  rptr.TriggerRun(); // Prevents from using `const` specifier in parameter
402  return std::unique_ptr<RMergeableValue<T>>{
403  static_cast<RMergeableValue<T> *>(rptr.fActionPtr->GetMergeableValue().release())};
404 }
405 } // namespace RDF
406 } // namespace Detail
407 } // namespace ROOT
408 
409 #endif // ROOT_TRESULTPROXY
ROOT::RDF::RResultPtr::RResultPtr
RResultPtr(const RResultPtr &)=default
c
#define c(i)
Definition: RSha256.hxx:101
ROOT::Detail::RDF::RLoopManager::Run
void Run()
Start the event loop with a different mechanism depending on IMT/no IMT, data source/no data source.
Definition: RLoopManager.cxx:561
GraphCreatorHelper
Helper class that provides the operation graph nodes.
ROOT::TypeTraits
ROOT type_traits extensions.
Definition: TypeTraits.hxx:21
ROOT::RDF::RResultPtr::kOnce
static constexpr ULong64_t kOnce
Convenience definition to express a callback must be executed once.
Definition: RResultPtr.hxx:153
ROOT::RDF::RResultPtr::TriggerRun
void TriggerRun()
Triggers the event loop in the RLoopManager.
Definition: RResultPtr.hxx:322
ROOT::RDF::RResultHandle
Definition: RResultHandle.hxx:27
ROOT::RDF::RResultPtr::fLoopManager
RDFDetail::RLoopManager * fLoopManager
Non-owning pointer to the RLoopManager at the root of this computation graph.
Definition: RResultPtr.hxx:126
ROOT::RDF::operator!=
bool operator!=(const RResultPtr< T1 > &lhs, const RResultPtr< T2 > &rhs)
Definition: RResultPtr.hxx:334
r
ROOT::R::TRInterface & r
Definition: Object.C:4
ROOT::RDF::RResultPtr::OnPartialResultSlot
RResultPtr< T > & OnPartialResultSlot(ULong64_t everyNEvents, std::function< void(unsigned int, T &)> callback)
Register a callback that RDataFrame will execute in each worker thread concurrently on that thread's ...
Definition: RResultPtr.hxx:294
ROOT::RDF::RResultPtr::RResultPtr
RResultPtr()=default
ROOT::RDF::RResultPtr::fActionPtr
std::shared_ptr< RDFInternal::RActionBase > fActionPtr
Owning pointer to the action that will produce this result.
Definition: RResultPtr.hxx:130
ROOT::Detail::RDF::GetMergeableValue
std::unique_ptr< RMergeableValue< T > > GetMergeableValue(RResultPtr< T > &rptr)
Retrieve a mergeable value from an RDataFrame action.
Definition: RResultPtr.hxx:397
ROOT::RDF::RResultPtr
Smart pointer for the return type of actions.
Definition: RResultPtr.hxx:79
v
@ v
Definition: rootcling_impl.cxx:3635
ROOT::RDF::RResultPtr::operator->
T * operator->()
Get a pointer to the encapsulated object.
Definition: RResultPtr.hxx:186
ROOT::Detail::RDF
Definition: GraphUtils.hxx:28
bool
ROOT::Internal::RDF::GraphDrawing::GraphCreatorHelper
Definition: GraphUtils.hxx:56
ROOT::Detail::RDF::MakeResultPtr
RResultPtr< T > MakeResultPtr(const std::shared_ptr< T > &r, RLoopManager &df, std::shared_ptr< ROOT::Internal::RDF::RActionBase > actionPtr)
Create a RResultPtr and set its pointer to the corresponding RAction This overload is invoked by non-...
Definition: RResultPtr.hxx:371
ROOT::RDF::RResultPtr::fObjPtr
SPT_t fObjPtr
Shared pointer encapsulating the wrapped result.
Definition: RResultPtr.hxx:127
ROOT::RDF::RResultPtr::OnPartialResult
RResultPtr< T > & OnPartialResult(ULong64_t everyNEvents, std::function< void(T &)> callback)
Register a callback that RDataFrame will execute "everyNEvents" on a partial result.
Definition: RResultPtr.hxx:249
ROOT::RDF::RResultPtr::IsReady
bool IsReady() const
Check whether the result has already been computed.
Definition: RResultPtr.hxx:315
ROOT::Detail::RDF::RMergeableValue
A result of an RDataFrame execution, that knows how to merge with other results of the same type.
Definition: RMergeableValue.hxx:129
ROOT::RDF::RResultPtr::end
RIterationHelper< T >::Iterator_t end()
Return an iterator to the end of the contained object if this makes sense, throw a compilation error ...
Definition: RResultPtr.hxx:199
ROOT::RDF::RResultPtr::GetPtr
T * GetPtr()
Get the pointer to the encapsulated object.
Definition: RResultPtr.hxx:177
ROOT::R::function
void function(const Char_t *name_, T fun, const Char_t *docstring=0)
Definition: RExports.h:151
ROOT::RDF::RResultPtr::SPT_t
std::shared_ptr< T > SPT_t
Definition: RResultPtr.hxx:81
TypeTraits.hxx
ROOT::RDF::RResultPtr::Value_t
T Value_t
Convenience alias to simplify access to proxied type.
Definition: RResultPtr.hxx:152
ROOT::RDF::RResultPtr::RResultPtr
RResultPtr(std::shared_ptr< T > objPtr, RDFDetail::RLoopManager *lm, std::shared_ptr< RDFInternal::RActionBase > actionPtr)
Definition: RResultPtr.hxx:145
void
typedef void((*Func_t)())
ROOT::RDF::RResultPtr::operator*
T & operator*()
Get a pointer to the encapsulated object.
Definition: RResultPtr.hxx:181
ROOT::RDF::RResultPtr::operator=
RResultPtr & operator=(RResultPtr &&)=default
ULong64_t
unsigned long long ULong64_t
Definition: RtypesCore.h:74
ROOT::RDF::RResultPtr::operator!=
friend bool operator!=(const RResultPtr< T1 > &lhs, const RResultPtr< T2 > &rhs)
Definition: RResultPtr.hxx:334
RtypesCore.h
ROOT::RDF::RResultPtr::GetValue
const T & GetValue()
Get a const reference to the encapsulated object.
Definition: RResultPtr.hxx:173
ROOT::RDF::RResultPtr::Get
T * Get()
Get the pointer to the encapsulated result.
Definition: RResultPtr.hxx:138
RActionBase.hxx
ROOT::Internal::RDF
Definition: RArrowDS.hxx:15
ROOT::RDF::RResultPtr::operator=
RResultPtr & operator=(const RResultPtr &)=default
ROOT::Math::Chebyshev::T
double T(double x)
Definition: ChebyshevPol.h:34
ROOT::Detail::RDF::RLoopManager::RegisterCallback
void RegisterCallback(ULong64_t everyNEvents, std::function< void(unsigned int)> &&f)
Definition: RLoopManager.cxx:649
ROOT::RDF::RResultPtr::begin
RIterationHelper< T >::Iterator_t begin()
Return an iterator to the beginning of the contained object if this makes sense, throw a compilation ...
Definition: RResultPtr.hxx:190
RLoopManager.hxx
type
int type
Definition: TGX11.cxx:121
ROOT::Detail::RDF::RLoopManager::GetNSlots
unsigned int GetNSlots() const
Definition: RLoopManager.hxx:161
ROOT
VSD Structures.
Definition: StringConv.hxx:21
ROOT::RDF::RResultPtr::operator==
friend bool operator==(const RResultPtr< T1 > &lhs, const RResultPtr< T2 > &rhs)
Definition: RResultPtr.hxx:328
ROOT::RDF::RResultPtr::RResultPtr
RResultPtr(RResultPtr &&)=default
ROOT::Detail::RDF::RLoopManager
The head node of a RDF computation graph.
Definition: RLoopManager.hxx:57
ROOT::RDF::operator==
bool operator==(const RResultPtr< T1 > &lhs, const RResultPtr< T2 > &rhs)
Definition: RResultPtr.hxx:328
TError.h