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