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