Logo ROOT   6.16/01
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-2018, 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
16#include "ROOT/TypeTraits.hxx"
17#include "TError.h" // Warning
18
19#include <memory>
20#include <functional>
21
22namespace ROOT {
23namespace Internal {
24namespace RDF {
26}
27}
28}
29
30namespace ROOT {
31namespace RDF {
32// Fwd decl for MakeResultPtr
33template <typename T>
34class RResultPtr;
35
36} // ns RDF
37
38namespace Detail {
39namespace RDF {
41// Fwd decl for RResultPtr
42template <typename T>
43RResultPtr<T> MakeResultPtr(const std::shared_ptr<T> &r, RLoopManager &df,
44 std::shared_ptr<ROOT::Internal::RDF::RActionBase> actionPtr);
45} // ns RDF
46} // ns Detail
47namespace RDF {
50namespace TTraits = ROOT::TypeTraits;
51
52/// Smart pointer for the return type of actions
53/**
54\class ROOT::RDF::RResultPtr
55\ingroup dataframe
56\brief A wrapper around the result of RDataFrame actions able to trigger calculations lazily.
57\tparam T Type of the action result
58
59A smart pointer which allows to access the result of a RDataFrame action. The
60methods of the encapsulated object can be accessed via the arrow operator.
61Upon invocation of the arrow operator or dereferencing (`operator*`), the
62loop on the events and calculations of all scheduled actions are executed
63if needed.
64It is possible to iterate on the result proxy if the proxied object is a collection.
65~~~{.cpp}
66for (auto& myItem : myResultProxy) { ... };
67~~~
68If iteration is not supported by the type of the proxied object, a compilation error is thrown.
69
70*/
71template <typename T>
73 // private using declarations
74 using SPT_t = std::shared_ptr<T>;
75
76 // friend declarations
77 template <typename T1>
79 std::shared_ptr<RDFInternal::RActionBase>);
80 template <class T1, class T2>
81 friend bool operator==(const RResultPtr<T1> &lhs, const RResultPtr<T2> &rhs);
82 template <class T1, class T2>
83 friend bool operator!=(const RResultPtr<T1> &lhs, const RResultPtr<T2> &rhs);
84 template <class T1>
85 friend bool operator==(const RResultPtr<T1> &lhs, std::nullptr_t rhs);
86 template <class T1>
87 friend bool operator==(std::nullptr_t lhs, const RResultPtr<T1> &rhs);
88 template <class T1>
89 friend bool operator!=(const RResultPtr<T1> &lhs, std::nullptr_t rhs);
90 template <class T1>
91 friend bool operator!=(std::nullptr_t lhs, const RResultPtr<T1> &rhs);
92
94
95 /// \cond HIDDEN_SYMBOLS
96 template <typename V, bool hasBeginEnd = TTraits::HasBeginAndEnd<V>::value>
97 struct RIterationHelper {
98 using Iterator_t = void;
99 void GetBegin(const V &) { static_assert(sizeof(V) == 0, "It does not make sense to ask begin for this class."); }
100 void GetEnd(const V &) { static_assert(sizeof(V) == 0, "It does not make sense to ask end for this class."); }
101 };
102
103 template <typename V>
104 struct RIterationHelper<V, true> {
105 using Iterator_t = decltype(std::begin(std::declval<V>()));
106 static Iterator_t GetBegin(const V &v) { return std::begin(v); };
107 static Iterator_t GetEnd(const V &v) { return std::end(v); };
108 };
109 /// \endcond
110
111 /// Non-owning pointer to the RLoopManager at the root of this computation graph.
112 /// The RLoopManager is guaranteed to be always in scope if fLoopManager is not a nullptr.
114 SPT_t fObjPtr; ///< Shared pointer encapsulating the wrapped result
115 /// Owning pointer to the action that will produce this result.
116 /// Ownership is shared with other copies of this ResultPtr.
117 std::shared_ptr<RDFInternal::RActionBase> fActionPtr;
118
119 /// Triggers the event loop in the RLoopManager
120 void TriggerRun();
121
122 /// Get the pointer to the encapsulated result.
123 /// Ownership is not transferred to the caller.
124 /// Triggers event loop and execution of all actions booked in the associated RLoopManager.
126 {
127 if (!fActionPtr->HasRun())
128 TriggerRun();
129 return fObjPtr.get();
130 }
131
132 RResultPtr(std::shared_ptr<T> objPtr, RDFDetail::RLoopManager *lm,
133 std::shared_ptr<RDFInternal::RActionBase> actionPtr)
134 : fLoopManager(lm), fObjPtr(std::move(objPtr)), fActionPtr(std::move(actionPtr))
135 {
136 }
137
138public:
139 using Value_t = T; ///< Convenience alias to simplify access to proxied type
140 static constexpr ULong64_t kOnce = 0ull; ///< Convenience definition to express a callback must be executed once
141
142 RResultPtr() = default;
143 RResultPtr(const RResultPtr &) = default;
144 RResultPtr(RResultPtr &&) = default;
145 RResultPtr &operator=(const RResultPtr &) = default;
147 explicit operator bool() const { return bool(fObjPtr); }
148
149 /// Get a const reference to the encapsulated object.
150 /// Triggers event loop and execution of all actions booked in the associated RLoopManager.
151 const T &GetValue() { return *Get(); }
152
153 /// Get the pointer to the encapsulated object.
154 /// Triggers event loop and execution of all actions booked in the associated RLoopManager.
155 T *GetPtr() { return Get(); }
156
157 /// Get a pointer to the encapsulated object.
158 /// Triggers event loop and execution of all actions booked in the associated RLoopManager.
159 T &operator*() { return *Get(); }
160
161 /// Get a pointer to the encapsulated object.
162 /// Ownership is not transferred to the caller.
163 /// Triggers event loop and execution of all actions booked in the associated RLoopManager.
164 T *operator->() { return Get(); }
165
166 /// Return an iterator to the beginning of the contained object if this makes
167 /// sense, throw a compilation error otherwise
168 typename RIterationHelper<T>::Iterator_t begin()
169 {
170 if (!fActionPtr->HasRun())
171 TriggerRun();
172 return RIterationHelper<T>::GetBegin(*fObjPtr);
173 }
174
175 /// Return an iterator to the end of the contained object if this makes
176 /// sense, throw a compilation error otherwise
177 typename RIterationHelper<T>::Iterator_t end()
178 {
179 if (!fActionPtr->HasRun())
180 TriggerRun();
181 return RIterationHelper<T>::GetEnd(*fObjPtr);
182 }
183
184 // clang-format off
185 /// Register a callback that RDataFrame will execute "everyNEvents" on a partial result.
186 ///
187 /// \param[in] everyNEvents Frequency at which the callback will be called, as a number of events processed
188 /// \param[in] callback a callable with signature `void(Value_t&)` where Value_t is the type of the value contained in this RResultPtr
189 /// \return this RResultPtr, to allow chaining of OnPartialResultSlot with other calls
190 ///
191 /// The callback must be a callable (lambda, function, functor class...) that takes a reference to the result type as
192 /// argument and returns nothing. RDataFrame will invoke registered callbacks passing partial action results as
193 /// arguments to them (e.g. a histogram filled with a part of the selected events, a counter incremented only up to a
194 /// certain point, a mean over a subset of the events and so forth).
195 ///
196 /// Callbacks can be used e.g. to inspect partial results of the analysis while the event loop is running. For
197 /// example one can draw an up-to-date version of a result histogram every 100 entries like this:
198 /// \code{.cpp}
199 /// auto h = tdf.Histo1D("x");
200 /// TCanvas c("c","x hist");
201 /// h.OnPartialResult(100, [&c](TH1D &h_) { c.cd(); h_.Draw(); c.Update(); });
202 /// h->Draw(); // event loop runs here, this `Draw` is executed after the event loop is finished
203 /// \endcode
204 ///
205 /// A value of 0 for everyNEvents indicates the callback must be executed only once, before running the event loop.
206 /// A conveniece definition `kOnce` is provided to make this fact more expressive in user code (see snippet below).
207 /// Multiple callbacks can be registered with the same RResultPtr (i.e. results of RDataFrame actions) and will
208 /// be executed sequentially. Callbacks are executed in the order they were registered.
209 /// The type of the value contained in a RResultPtr is also available as RResultPtr<T>::Value_t, e.g.
210 /// \code{.cpp}
211 /// auto h = tdf.Histo1D("x");
212 /// // h.kOnce is 0
213 /// // decltype(h)::Value_t is TH1D
214 /// \endcode
215 ///
216 /// When implicit multi-threading is enabled, the callback:
217 /// - will never be executed by multiple threads concurrently: it needs not be thread-safe. For example the snippet
218 /// above that draws the partial histogram on a canvas works seamlessly in multi-thread event loops.
219 /// - will always be executed "everyNEvents": partial results will "contain" that number of events more from
220 /// one call to the next
221 /// - might be executed by a different worker thread at different times: the value of `std::this_thread::get_id()`
222 /// might change between calls
223 /// To register a callback that is called by _each_ worker thread (concurrently) every N events one can use
224 /// OnPartialResultSlot.
225 // clang-format on
226 RResultPtr<T> &OnPartialResult(ULong64_t everyNEvents, std::function<void(T &)> callback)
227 {
228 const auto nSlots = fLoopManager->GetNSlots();
229 auto actionPtr = fActionPtr;
230 auto c = [nSlots, actionPtr, callback](unsigned int slot) {
231 if (slot != nSlots - 1)
232 return;
233 auto partialResult = static_cast<Value_t *>(actionPtr->PartialUpdate(slot));
234 callback(*partialResult);
235 };
236 fLoopManager->RegisterCallback(everyNEvents, std::move(c));
237 return *this;
238 }
239
240 // clang-format off
241 /// Register a callback that RDataFrame will execute in each worker thread concurrently on that thread's partial result.
242 ///
243 /// \param[in] everyNEvents Frequency at which the callback will be called by each thread, as a number of events processed
244 /// \param[in] a callable with signature `void(unsigned int, 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 /// See `OnPartialResult` for a generic explanation of the callback mechanism.
248 /// Compared to `OnPartialResult`, this method has two major differences:
249 /// - all worker threads invoke the callback once every specified number of events. The event count is per-thread,
250 /// and callback invocation might happen concurrently (i.e. the callback must be thread-safe)
251 /// - the callable must take an extra `unsigned int` parameter corresponding to a multi-thread "processing slot":
252 /// this is a "helper value" to simplify writing thread-safe callbacks: different worker threads might invoke the
253 /// callback concurrently but always with different `slot` numbers.
254 /// - a value of 0 for everyNEvents indicates the callback must be executed once _per slot_.
255 ///
256 /// For example, the following snippet prints out a thread-safe progress bar of the events processed by RDataFrame
257 /// \code
258 /// auto c = tdf.Count(); // any action would do, but `Count` is the most lightweight
259 /// std::string progress;
260 /// std::mutex bar_mutex;
261 /// c.OnPartialResultSlot(nEvents / 100, [&progress, &bar_mutex](unsigned int, ULong64_t &) {
262 /// std::lock_guard<std::mutex> lg(bar_mutex);
263 /// progress.push_back('#');
264 /// std::cout << "\r[" << std::left << std::setw(100) << progress << ']' << std::flush;
265 /// });
266 /// std::cout << "Analysis running..." << std::endl;
267 /// *c; // trigger the event loop by accessing an action's result
268 /// std::cout << "\nDone!" << std::endl;
269 /// \endcode
270 // clang-format on
271 RResultPtr<T> &OnPartialResultSlot(ULong64_t everyNEvents, std::function<void(unsigned int, T &)> callback)
272 {
273 auto actionPtr = fActionPtr;
274 auto c = [actionPtr, callback](unsigned int slot) {
275 auto partialResult = static_cast<Value_t *>(actionPtr->PartialUpdate(slot));
276 callback(slot, *partialResult);
277 };
278 fLoopManager->RegisterCallback(everyNEvents, std::move(c));
279 return *this;
280 }
281};
282
283template <typename T>
285{
286 fLoopManager->Run();
287}
288
289template <class T1, class T2>
290bool operator==(const RResultPtr<T1> &lhs, const RResultPtr<T2> &rhs)
291{
292 return lhs.fObjPtr == rhs.fObjPtr;
293}
294
295template <class T1, class T2>
296bool operator!=(const RResultPtr<T1> &lhs, const RResultPtr<T2> &rhs)
297{
298 return lhs.fObjPtr != rhs.fObjPtr;
299}
300
301template <class T1>
302bool operator==(const RResultPtr<T1> &lhs, std::nullptr_t rhs)
303{
304 return lhs.fObjPtr == rhs;
305}
306
307template <class T1>
308bool operator==(std::nullptr_t lhs, const RResultPtr<T1> &rhs)
309{
310 return lhs == rhs.fObjPtr;
311}
312
313template <class T1>
314bool operator!=(const RResultPtr<T1> &lhs, std::nullptr_t rhs)
315{
316 return lhs.fObjPtr != rhs;
317}
318
319template <class T1>
320bool operator!=(std::nullptr_t lhs, const RResultPtr<T1> &rhs)
321{
322 return lhs != rhs.fObjPtr;
323}
324
325} // end NS RDF
326
327namespace Detail {
328namespace RDF {
329/// Create a RResultPtr and set its pointer to the corresponding RAction
330/// This overload is invoked by non-jitted actions, as they have access to RAction before constructing RResultPtr.
331template <typename T>
332RResultPtr<T>
333MakeResultPtr(const std::shared_ptr<T> &r, RLoopManager &lm, std::shared_ptr<RDFInternal::RActionBase> actionPtr)
334{
335 return RResultPtr<T>(r, &lm, std::move(actionPtr));
336}
337} // end NS RDF
338} // end NS Detail
339} // end NS ROOT
340
341#endif // ROOT_TRESULTPROXY
SVector< double, 2 > v
Definition: Dict.h:5
ROOT::R::TRInterface & r
Definition: Object.C:4
#define c(i)
Definition: RSha256.hxx:101
unsigned long long ULong64_t
Definition: RtypesCore.h:70
typedef void((*Func_t)())
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)
Helper class that provides the operation graph nodes.
Smart pointer for the return type of actions.
Definition: RResultPtr.hxx:72
void TriggerRun()
Triggers the event loop in the RLoopManager.
Definition: RResultPtr.hxx:284
RResultPtr(RResultPtr &&)=default
T * GetPtr()
Get the pointer to the encapsulated object.
Definition: RResultPtr.hxx:155
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:226
RDFDetail::RLoopManager * fLoopManager
Non-owning pointer to the RLoopManager at the root of this computation graph.
Definition: RResultPtr.hxx:113
friend bool operator!=(const RResultPtr< T1 > &lhs, const RResultPtr< T2 > &rhs)
Definition: RResultPtr.hxx:296
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:168
T Value_t
Convenience alias to simplify access to proxied type.
Definition: RResultPtr.hxx:139
T * Get()
Get the pointer to the encapsulated result.
Definition: RResultPtr.hxx:125
const T & GetValue()
Get a const reference to the encapsulated object.
Definition: RResultPtr.hxx:151
RResultPtr(const RResultPtr &)=default
T & operator*()
Get a pointer to the encapsulated object.
Definition: RResultPtr.hxx:159
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:271
RResultPtr & operator=(RResultPtr &&)=default
friend bool operator==(const RResultPtr< T1 > &lhs, const RResultPtr< T2 > &rhs)
Definition: RResultPtr.hxx:290
std::shared_ptr< RDFInternal::RActionBase > fActionPtr
Owning pointer to the action that will produce this result.
Definition: RResultPtr.hxx:117
std::shared_ptr< T > SPT_t
Definition: RResultPtr.hxx:74
T * operator->()
Get a pointer to the encapsulated object.
Definition: RResultPtr.hxx:164
SPT_t fObjPtr
Shared pointer encapsulating the wrapped result.
Definition: RResultPtr.hxx:114
static constexpr ULong64_t kOnce
Convenience definition to express a callback must be executed once.
Definition: RResultPtr.hxx:140
RResultPtr(std::shared_ptr< T > objPtr, RDFDetail::RLoopManager *lm, std::shared_ptr< RDFInternal::RActionBase > actionPtr)
Definition: RResultPtr.hxx:132
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:177
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:333
double T(double x)
Definition: ChebyshevPol.h:34
bool operator!=(const RResultPtr< T1 > &lhs, const RResultPtr< T2 > &rhs)
Definition: RResultPtr.hxx:296
bool operator==(const RResultPtr< T1 > &lhs, const RResultPtr< T2 > &rhs)
Definition: RResultPtr.hxx:290
void function(const Char_t *name_, T fun, const Char_t *docstring=0)
Definition: RExports.h:151
ROOT type_traits extensions.
Definition: TypeTraits.hxx:23
Namespace for new ROOT classes and functions.
Definition: StringConv.hxx:21
STL namespace.