Logo ROOT  
Reference Guide
Loading...
Searching...
No Matches
RFieldProxiedCollection.hxx
Go to the documentation of this file.
1/// \file ROOT/RField/ProxiedCollection.hxx
2/// \author Jakob Blomer <jblomer@cern.ch>
3/// \date 2018-10-09
4
5/*************************************************************************
6 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
13#ifndef ROOT_RField_ProxiedCollection
14#define ROOT_RField_ProxiedCollection
15
16#ifndef ROOT_RField
17#error "Please include RField.hxx!"
18#endif
19
20#include <ROOT/RFieldBase.hxx>
21#include <ROOT/RNTupleTypes.hxx>
22
24
25#include <iterator>
26#include <map>
27#include <set>
28#include <string>
29#include <string_view>
30#include <type_traits>
31#include <unordered_map>
32#include <unordered_set>
33#include <vector>
34
35namespace ROOT {
36
37namespace Detail {
38class RFieldVisitor;
39} // namespace Detail
40
41/// The field for a class representing a collection of elements via TVirtualCollectionProxy.
42/// Objects of such type behave as collections that can be accessed through the corresponding member functions in
43/// TVirtualCollectionProxy. For STL collections, these proxies are provided. Custom classes need to implement the
44/// corresponding member functions in TVirtualCollectionProxy. At a bare minimum, the user is required to provide an
45/// implementation for the following functions in TVirtualCollectionProxy: HasPointers(), GetProperties(),
46/// GetValueClass(), GetType(), PushProxy(), PopProxy(), GetFunctionCreateIterators(), GetFunctionNext(),
47/// and GetFunctionDeleteTwoIterators().
48///
49/// The collection proxy for a given class can be set via TClass::CopyCollectionProxy().
51protected:
52 /// Allows for iterating over the elements of a proxied collection. RCollectionIterableOnce avoids an additional
53 /// iterator copy (see TVirtualCollectionProxy::GetFunctionCopyIterator) and thus can only be iterated once.
55 public:
61 static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk);
62
63 private:
64 class RIterator {
66 void *fIterator = nullptr;
67 void *fElementPtr = nullptr;
68
69 void Advance()
70 {
71 auto fnNext_Contig = [&]() {
72 // Array-backed collections (e.g. `kSTLvector`) directly use the pointer-to-iterator-data as a
73 // pointer-to-element, thus saving an indirection level (see documentation for TVirtualCollectionProxy)
74 auto &iter = reinterpret_cast<unsigned char *&>(fIterator), p = iter;
75 iter += fOwner.fStride;
76 return p;
77 };
78 fElementPtr = fOwner.fStride ? fnNext_Contig() : fOwner.fIFuncs.fNext(fIterator, fOwner.fEnd);
79 }
80
81 public:
82 using iterator_category = std::forward_iterator_tag;
84 using difference_type = std::ptrdiff_t;
85 using pointer = void *;
86
87 RIterator(const RCollectionIterableOnce &owner) : fOwner(owner) {}
88 RIterator(const RCollectionIterableOnce &owner, void *iter) : fOwner(owner), fIterator(iter) { Advance(); }
90 {
91 Advance();
92 return *this;
93 }
94 pointer operator*() const { return fElementPtr; }
95 bool operator!=(const iterator &rh) const { return fElementPtr != rh.fElementPtr; }
96 bool operator==(const iterator &rh) const { return fElementPtr == rh.fElementPtr; }
97 };
98
100 const std::size_t fStride;
105
106 public:
107 /// Construct a RCollectionIterableOnce that iterates over `collection`. If elements are guaranteed to be
108 /// contiguous in memory (e.g. a vector), `stride` can be provided for faster iteration, i.e. the address of each
109 /// element is known given the base pointer.
110 RCollectionIterableOnce(void *collection, const RIteratorFuncs &ifuncs, TVirtualCollectionProxy *proxy,
111 std::size_t stride = 0U)
112 : fIFuncs(ifuncs), fStride(stride)
113 {
114 fIFuncs.fCreateIterators(collection, &fBegin, &fEnd, proxy);
115 }
116 ~RCollectionIterableOnce() { fIFuncs.fDeleteTwoIterators(fBegin, fEnd); }
117
118 RIterator begin() { return RIterator(*this, fBegin); }
119 RIterator end() { return fStride ? RIterator(*this, fEnd) : RIterator(*this); }
120 }; // class RCollectionIterableOnce
121
123 private:
124 std::shared_ptr<TVirtualCollectionProxy> fProxy;
125 std::unique_ptr<RDeleter> fItemDeleter;
126 std::size_t fItemSize = 0;
128
129 public:
130 explicit RProxiedCollectionDeleter(std::shared_ptr<TVirtualCollectionProxy> proxy) : fProxy(proxy) {}
131 RProxiedCollectionDeleter(std::shared_ptr<TVirtualCollectionProxy> proxy, std::unique_ptr<RDeleter> itemDeleter,
132 size_t itemSize)
133 : fProxy(proxy), fItemDeleter(std::move(itemDeleter)), fItemSize(itemSize)
134 {
135 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
136 }
137 void operator()(void *objPtr, bool dtorOnly) final;
138 };
139
140 /// The collection proxy is needed by the deleters and thus defined as a shared pointer
141 std::shared_ptr<TVirtualCollectionProxy> fProxy;
144 /// Two sets of functions to operate on iterators, to be used depending on the access type. The direction preserves
145 /// the meaning from TVirtualCollectionProxy, i.e. read from disk / write to disk, respectively
148 std::size_t fItemSize;
150
151 /// Constructor used when the value type of the collection is not known in advance, i.e. in the case of custom
152 /// collections. Note that this constructor requires manual initialization of the item field
153 /// (Attach() and setting fItemSize)
154 RProxiedCollectionField(std::string_view fieldName, TClass *classp);
155
156 std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const override;
158 void GenerateColumns() final;
159 void GenerateColumns(const ROOT::RNTupleDescriptor &desc) final;
160
161 void ConstructValue(void *where) const final;
162 std::unique_ptr<RDeleter> GetDeleter() const final;
163
164 std::size_t AppendImpl(const void *from) final;
165 void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
166
167 void ReconcileOnDiskField(const RNTupleDescriptor &desc) override;
168
169 void CommitClusterImpl() final { fNWritten = 0; }
170
171public:
172 RProxiedCollectionField(std::string_view fieldName, std::string_view typeName);
175 ~RProxiedCollectionField() override = default;
176
177 std::vector<RValue> SplitValue(const RValue &value) const final;
178 size_t GetValueSize() const final { return fProxy->Sizeof(); }
179 size_t GetAlignment() const final { return alignof(std::max_align_t); }
180 void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
181};
182
183////////////////////////////////////////////////////////////////////////////////
184/// Template specializations for classes with collection proxies
185////////////////////////////////////////////////////////////////////////////////
186
187template <typename T, typename = void>
188struct HasCollectionProxyMemberType : std::false_type {
189};
190template <typename T>
192 T, typename std::enable_if<std::is_same<typename T::IsCollectionProxy, std::true_type>::value>::type>
193 : std::true_type {
194};
195
196/* The point here is that we can only tell at run time if a class has an associated collection proxy.
197For compile time, in the first iteration of this PR we had an extra template argument that acted as a "tag" to
198differentiate the RField specialization for classes with an associated collection proxy (inherits
199RProxiedCollectionField) from the RField primary template definition (RClassField-derived), as in:
200```
201auto field = std::make_unique<RField<MyClass>>("klass");
202// vs
203auto otherField = std::make_unique<RField<MyClass, ROOT::TagIsCollectionProxy>>("klass");
204```
205
206That is convenient only for non-nested types, i.e. it doesn't work with, e.g. `RField<std::vector<MyClass>,
207ROOT::TagIsCollectionProxy>`, as the tag is not forwarded to the instantiation of the inner RField
208(that for the value type of the vector). The following two possible solutions were considered:
209- A wrapper type that helps to differentiate both cases.
210There we would have:
211```
212auto field = std::make_unique<RField<RProxiedCollection<MyClass>>>("klass"); // Using collection proxy
213```
214- A helper IsCollectionProxy<T> type, that can be used in a similar way to those in the `<type_traits>` header.
215We found this more convenient and is the implemented thing below. Here, classes can be marked as a
216collection proxy with either of the following two forms (whichever is more convenient for the user):
217```
218template <>
219struct IsCollectionProxy<MyClass> : std::true_type {};
220```
221or by adding a member type to the class as follows:
222```
223class MyClass {
224public:
225 using IsCollectionProxy = std::true_type;
226};
227```
228
229Of course, there is another possible solution which is to have a single RClassField that implements both
230the regular-class and the collection-proxy behaviors, and always chooses appropriately at run time.
231We found that less clean and probably has more overhead, as most probably it involves an additional branch + call
232in each of the member functions. */
233/// Helper type trait for marking classes as a collection proxy.
234/// This type trait must be set for collection proxy-based RNTuple fields created through MakeField<T>.
235template <typename T, typename = void>
238
239/// Classes behaving as a collection of elements that can be queried via the TVirtualCollectionProxy interface
240/// The use of a collection proxy for a particular class can be enabled via:
241/// ```
242/// namespace ROOT {
243/// template <> struct IsCollectionProxy<Classname> : std::true_type {};
244/// }
245/// ```
246/// Alternatively, this can be achieved by adding a member type to the class definition as follows:
247/// ```
248/// class Classname {
249/// public:
250/// using IsCollectionProxy = std::true_type;
251/// };
252/// ```
253template <typename T>
254class RField<T, typename std::enable_if<IsCollectionProxy<T>::value>::type> final : public RProxiedCollectionField {
255public:
256 static std::string TypeName() { return ROOT::Internal::GetRenormalizedTypeName(typeid(T)); }
257 RField(std::string_view name) : RProxiedCollectionField(name, Internal::GetDemangledTypeName(typeid(T)))
258 {
259 static_assert(std::is_class<T>::value, "collection proxy unsupported for fundamental types");
260 }
261 RField(RField &&other) = default;
262 RField &operator=(RField &&other) = default;
263 ~RField() final = default;
264};
265
266////////////////////////////////////////////////////////////////////////////////
267/// Template specializations for C++ std::[unordered_][multi]map
268////////////////////////////////////////////////////////////////////////////////
269
270/// The generic field for a `std::map<KeyType, ValueType>` and `std::unordered_map<KeyType, ValueType>`
272public:
273 enum class EMapType {
274 kMap,
275 kUnorderedMap,
276 kMultiMap,
277 kUnorderedMultiMap
278 };
279
280private:
282
283protected:
284 std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
285 void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;
286
287public:
288 RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField);
289 RMapField(RMapField &&other) = default;
290 RMapField &operator=(RMapField &&other) = default;
291 ~RMapField() override = default;
292};
293
294template <typename KeyT, typename ValueT>
295class RField<std::map<KeyT, ValueT>> final : public RMapField {
296public:
297 static std::string TypeName()
298 {
299 return "std::map<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
300 }
301
302 explicit RField(std::string_view name)
303 : RMapField(name, EMapType::kMap, std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
304 {
305 }
306 RField(RField &&other) = default;
307 RField &operator=(RField &&other) = default;
308 ~RField() final = default;
309};
310
311template <typename KeyT, typename ValueT>
312class RField<std::unordered_map<KeyT, ValueT>> final : public RMapField {
313public:
314 static std::string TypeName()
315 {
316 return "std::unordered_map<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
317 }
318
319 explicit RField(std::string_view name)
320 : RMapField(name, EMapType::kUnorderedMap, std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
321 {
322 }
323 RField(RField &&other) = default;
324 RField &operator=(RField &&other) = default;
325 ~RField() final = default;
326};
327
328template <typename KeyT, typename ValueT>
329class RField<std::multimap<KeyT, ValueT>> final : public RMapField {
330public:
331 static std::string TypeName()
332 {
333 return "std::multimap<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
334 }
335
336 explicit RField(std::string_view name)
337 : RMapField(name, EMapType::kMultiMap, std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
338 {
339 }
340 RField(RField &&other) = default;
341 RField &operator=(RField &&other) = default;
342 ~RField() final = default;
343};
344
345template <typename KeyT, typename ValueT>
346class RField<std::unordered_multimap<KeyT, ValueT>> final : public RMapField {
347public:
348 static std::string TypeName()
349 {
350 return "std::unordered_multimap<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
351 }
352
353 explicit RField(std::string_view name)
354 : RMapField(name, EMapType::kUnorderedMultiMap, std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
355 {
356 }
357 RField(RField &&other) = default;
358 RField &operator=(RField &&other) = default;
359 ~RField() final = default;
360};
361
362////////////////////////////////////////////////////////////////////////////////
363/// Template specializations for C++ std::[unordered_][multi]set
364////////////////////////////////////////////////////////////////////////////////
365
366/// The generic field for a `std::set<Type>` and `std::unordered_set<Type>`
368public:
369 enum class ESetType {
370 kSet,
371 kUnorderedSet,
372 kMultiSet,
373 kUnorderedMultiSet
374 };
375
376private:
378
379protected:
380 std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
381 void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;
382
383public:
384 RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField);
385 RSetField(RSetField &&other) = default;
386 RSetField &operator=(RSetField &&other) = default;
387 ~RSetField() override = default;
388};
389
390template <typename ItemT>
391class RField<std::set<ItemT>> final : public RSetField {
392public:
393 static std::string TypeName() { return "std::set<" + RField<ItemT>::TypeName() + ">"; }
394
395 explicit RField(std::string_view name) : RSetField(name, ESetType::kSet, std::make_unique<RField<ItemT>>("_0")) {}
396 RField(RField &&other) = default;
397 RField &operator=(RField &&other) = default;
398 ~RField() final = default;
399};
400
401template <typename ItemT>
402class RField<std::unordered_set<ItemT>> final : public RSetField {
403public:
404 static std::string TypeName() { return "std::unordered_set<" + RField<ItemT>::TypeName() + ">"; }
405
406 explicit RField(std::string_view name)
407 : RSetField(name, ESetType::kUnorderedSet, std::make_unique<RField<ItemT>>("_0"))
408 {
409 }
410 RField(RField &&other) = default;
411 RField &operator=(RField &&other) = default;
412 ~RField() final = default;
413};
414
415template <typename ItemT>
416class RField<std::multiset<ItemT>> final : public RSetField {
417public:
418 static std::string TypeName() { return "std::multiset<" + RField<ItemT>::TypeName() + ">"; }
419
420 explicit RField(std::string_view name) : RSetField(name, ESetType::kMultiSet, std::make_unique<RField<ItemT>>("_0"))
421 {
422 }
423 RField(RField &&other) = default;
424 RField &operator=(RField &&other) = default;
425 ~RField() final = default;
426};
427
428template <typename ItemT>
429class RField<std::unordered_multiset<ItemT>> final : public RSetField {
430public:
431 static std::string TypeName() { return "std::unordered_multiset<" + RField<ItemT>::TypeName() + ">"; }
432
433 explicit RField(std::string_view name)
434 : RSetField(name, ESetType::kUnorderedMultiSet, std::make_unique<RField<ItemT>>("_0"))
435 {
436 }
437 RField(RField &&other) = default;
438 RField &operator=(RField &&other) = default;
439 ~RField() final = default;
440};
441
442} // namespace ROOT
443
444#endif
int Int_t
Signed integer 4 bytes (int).
Definition RtypesCore.h:59
char name[80]
Definition TGX11.cxx:148
Binding & operator=(OUT(*fun)(void))
Abstract base class for classes implementing the visitor design pattern.
The in-memory representation of a 32bit or 64bit on-disk index column.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
The list of column representations a field can have.
A functor to release the memory acquired by CreateValue() (memory and constructor).
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
RFieldBase(std::string_view name, std::string_view type, ROOT::ENTupleStructure structure, bool isSimple, std::size_t nRepetitions=0)
The constructor creates the underlying column objects and connects them to either a sink or a source.
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:320
~RField() final=default
RField & operator=(RField &&other)=default
static std::string TypeName()
Definition RField.hxx:322
RField(std::string_view name)
Definition RField.hxx:323
Template specializations for C++ std::[unordered_][multi]map.
RMapField(RMapField &&other)=default
RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr< RFieldBase > itemField)
~RMapField() override=default
RMapField & operator=(RMapField &&other)=default
The on-storage metadata of an RNTuple.
unsigned char fEndSmallBuf[TVirtualCollectionProxy::fgIteratorArenaSize]
RCollectionIterableOnce(void *collection, const RIteratorFuncs &ifuncs, TVirtualCollectionProxy *proxy, std::size_t stride=0U)
Construct a RCollectionIterableOnce that iterates over collection.
static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk)
unsigned char fBeginSmallBuf[TVirtualCollectionProxy::fgIteratorArenaSize]
RProxiedCollectionDeleter(std::shared_ptr< TVirtualCollectionProxy > proxy, std::unique_ptr< RDeleter > itemDeleter, size_t itemSize)
RProxiedCollectionDeleter(std::shared_ptr< TVirtualCollectionProxy > proxy)
void operator()(void *objPtr, bool dtorOnly) final
The field for a class representing a collection of elements via TVirtualCollectionProxy.
RProxiedCollectionField & operator=(RProxiedCollectionField &&other)=default
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
~RProxiedCollectionField() override=default
RProxiedCollectionField(RProxiedCollectionField &&other)=default
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
RProxiedCollectionField(std::string_view fieldName, TClass *classp)
Constructor used when the value type of the collection is not known in advance, i....
RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite
ROOT::Internal::RColumnIndex fNWritten
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RCollectionIterableOnce::RIteratorFuncs fIFuncsRead
Two sets of functions to operate on iterators, to be used depending on the access type.
std::shared_ptr< TVirtualCollectionProxy > fProxy
The collection proxy is needed by the deleters and thus defined as a shared pointer.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) override
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
Template specializations for C++ std::[unordered_][multi]set.
RSetField & operator=(RSetField &&other)=default
~RSetField() override=default
RSetField(RSetField &&other)=default
RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr< RFieldBase > itemField)
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
Defines a common interface to inspect/change the contents of an object that represents a collection.
void(* CreateIterators_t)(void *collection, void **begin_arena, void **end_arena, TVirtualCollectionProxy *proxy)
*begin_arena and *end_arena should contain the location of a memory arena of size fgIteratorArenaSize...
void *(* Next_t)(void *iter, const void *end)
iter and end should be pointers to an iterator to be incremented and an iterator that points to the e...
void(* DeleteTwoIterators_t)(void *begin, void *end)
static const Int_t fgIteratorArenaSize
The size of a small buffer that can be allocated on the stack to store iterator-specific information.
STL class.
STL class.
STL class.
STL class.
Special implementation of ROOT::RRangeCast for TCollection, including a check that the cast target ty...
Definition TObject.h:395
std::string GetDemangledTypeName(const std::type_info &)
Returns a string with the demangled and normalized name for the given type.
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
@ kUnorderedMultiSet
Definition TClassEdit.h:105
@ kUnorderedMultiMap
Definition TClassEdit.h:107
Template specializations for classes with collection proxies.
Helper type trait for marking classes as a collection proxy.