Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 09:14:20

0001 /// \file ROOT/RField/ProxiedCollection.hxx
0002 /// \ingroup NTuple
0003 /// \author Jakob Blomer <jblomer@cern.ch>
0004 /// \date 2018-10-09
0005 
0006 /*************************************************************************
0007  * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
0008  * All rights reserved.                                                  *
0009  *                                                                       *
0010  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0011  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0012  *************************************************************************/
0013 
0014 #ifndef ROOT_RField_ProxiedCollection
0015 #define ROOT_RField_ProxiedCollection
0016 
0017 #ifndef ROOT_RField
0018 #error "Please include RField.hxx!"
0019 #endif
0020 
0021 #include <ROOT/RFieldBase.hxx>
0022 #include <ROOT/RNTupleUtil.hxx>
0023 
0024 #include <TVirtualCollectionProxy.h>
0025 
0026 #include <iterator>
0027 #include <map>
0028 #include <set>
0029 #include <string>
0030 #include <string_view>
0031 #include <type_traits>
0032 #include <unordered_map>
0033 #include <unordered_set>
0034 #include <vector>
0035 
0036 namespace ROOT {
0037 
0038 namespace Detail {
0039 class RFieldVisitor;
0040 } // namespace Detail
0041 
0042 /// The field for a class representing a collection of elements via TVirtualCollectionProxy.
0043 /// Objects of such type behave as collections that can be accessed through the corresponding member functions in
0044 /// TVirtualCollectionProxy. For STL collections, these proxies are provided. Custom classes need to implement the
0045 /// corresponding member functions in TVirtualCollectionProxy. At a bare minimum, the user is required to provide an
0046 /// implementation for the following functions in TVirtualCollectionProxy: HasPointers(), GetProperties(),
0047 /// GetValueClass(), GetType(), PushProxy(), PopProxy(), GetFunctionCreateIterators(), GetFunctionNext(),
0048 /// and GetFunctionDeleteTwoIterators().
0049 ///
0050 /// The collection proxy for a given class can be set via TClass::CopyCollectionProxy().
0051 class RProxiedCollectionField : public RFieldBase {
0052 protected:
0053    /// Allows for iterating over the elements of a proxied collection. RCollectionIterableOnce avoids an additional
0054    /// iterator copy (see TVirtualCollectionProxy::GetFunctionCopyIterator) and thus can only be iterated once.
0055    class RCollectionIterableOnce {
0056    public:
0057       struct RIteratorFuncs {
0058          TVirtualCollectionProxy::CreateIterators_t fCreateIterators;
0059          TVirtualCollectionProxy::DeleteTwoIterators_t fDeleteTwoIterators;
0060          TVirtualCollectionProxy::Next_t fNext;
0061       };
0062       static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk);
0063 
0064    private:
0065       class RIterator {
0066          const RCollectionIterableOnce &fOwner;
0067          void *fIterator = nullptr;
0068          void *fElementPtr = nullptr;
0069 
0070          void Advance()
0071          {
0072             auto fnNext_Contig = [&]() {
0073                // Array-backed collections (e.g. `kSTLvector`) directly use the pointer-to-iterator-data as a
0074                // pointer-to-element, thus saving an indirection level (see documentation for TVirtualCollectionProxy)
0075                auto &iter = reinterpret_cast<unsigned char *&>(fIterator), p = iter;
0076                iter += fOwner.fStride;
0077                return p;
0078             };
0079             fElementPtr = fOwner.fStride ? fnNext_Contig() : fOwner.fIFuncs.fNext(fIterator, fOwner.fEnd);
0080          }
0081 
0082       public:
0083          using iterator_category = std::forward_iterator_tag;
0084          using iterator = RIterator;
0085          using difference_type = std::ptrdiff_t;
0086          using pointer = void *;
0087 
0088          RIterator(const RCollectionIterableOnce &owner) : fOwner(owner) {}
0089          RIterator(const RCollectionIterableOnce &owner, void *iter) : fOwner(owner), fIterator(iter) { Advance(); }
0090          iterator operator++()
0091          {
0092             Advance();
0093             return *this;
0094          }
0095          pointer operator*() const { return fElementPtr; }
0096          bool operator!=(const iterator &rh) const { return fElementPtr != rh.fElementPtr; }
0097          bool operator==(const iterator &rh) const { return fElementPtr == rh.fElementPtr; }
0098       };
0099 
0100       const RIteratorFuncs &fIFuncs;
0101       const std::size_t fStride;
0102       unsigned char fBeginSmallBuf[TVirtualCollectionProxy::fgIteratorArenaSize];
0103       unsigned char fEndSmallBuf[TVirtualCollectionProxy::fgIteratorArenaSize];
0104       void *fBegin = &fBeginSmallBuf;
0105       void *fEnd = &fEndSmallBuf;
0106 
0107    public:
0108       /// Construct a RCollectionIterableOnce that iterates over `collection`.  If elements are guaranteed to be
0109       /// contiguous in memory (e.g. a vector), `stride` can be provided for faster iteration, i.e. the address of each
0110       /// element is known given the base pointer.
0111       RCollectionIterableOnce(void *collection, const RIteratorFuncs &ifuncs, TVirtualCollectionProxy *proxy,
0112                               std::size_t stride = 0U)
0113          : fIFuncs(ifuncs), fStride(stride)
0114       {
0115          fIFuncs.fCreateIterators(collection, &fBegin, &fEnd, proxy);
0116       }
0117       ~RCollectionIterableOnce() { fIFuncs.fDeleteTwoIterators(fBegin, fEnd); }
0118 
0119       RIterator begin() { return RIterator(*this, fBegin); }
0120       RIterator end() { return fStride ? RIterator(*this, fEnd) : RIterator(*this); }
0121    }; // class RCollectionIterableOnce
0122 
0123    class RProxiedCollectionDeleter : public RDeleter {
0124    private:
0125       std::shared_ptr<TVirtualCollectionProxy> fProxy;
0126       std::unique_ptr<RDeleter> fItemDeleter;
0127       std::size_t fItemSize = 0;
0128       RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite;
0129 
0130    public:
0131       explicit RProxiedCollectionDeleter(std::shared_ptr<TVirtualCollectionProxy> proxy) : fProxy(proxy) {}
0132       RProxiedCollectionDeleter(std::shared_ptr<TVirtualCollectionProxy> proxy, std::unique_ptr<RDeleter> itemDeleter,
0133                                 size_t itemSize)
0134          : fProxy(proxy), fItemDeleter(std::move(itemDeleter)), fItemSize(itemSize)
0135       {
0136          fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
0137       }
0138       void operator()(void *objPtr, bool dtorOnly) final;
0139    };
0140 
0141    /// The collection proxy is needed by the deleters and thus defined as a shared pointer
0142    std::shared_ptr<TVirtualCollectionProxy> fProxy;
0143    Int_t fProperties;
0144    Int_t fCollectionType;
0145    /// Two sets of functions to operate on iterators, to be used depending on the access type.  The direction preserves
0146    /// the meaning from TVirtualCollectionProxy, i.e. read from disk / write to disk, respectively
0147    RCollectionIterableOnce::RIteratorFuncs fIFuncsRead;
0148    RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite;
0149    std::size_t fItemSize;
0150    ROOT::Internal::RColumnIndex fNWritten;
0151 
0152    /// Constructor used when the value type of the collection is not known in advance, i.e. in the case of custom
0153    /// collections.
0154    RProxiedCollectionField(std::string_view fieldName, TClass *classp);
0155    /// Constructor used when the value type of the collection is known in advance, e.g. in RSetField.
0156    RProxiedCollectionField(std::string_view fieldName, std::string_view typeName,
0157                            std::unique_ptr<RFieldBase> itemField);
0158 
0159 protected:
0160    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0161    const RColumnRepresentations &GetColumnRepresentations() const final;
0162    void GenerateColumns() final;
0163    void GenerateColumns(const ROOT::RNTupleDescriptor &desc) final;
0164 
0165    void ConstructValue(void *where) const final;
0166    std::unique_ptr<RDeleter> GetDeleter() const final;
0167 
0168    std::size_t AppendImpl(const void *from) final;
0169    void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
0170 
0171    void CommitClusterImpl() final { fNWritten = 0; }
0172 
0173 public:
0174    RProxiedCollectionField(std::string_view fieldName, std::string_view typeName);
0175    RProxiedCollectionField(RProxiedCollectionField &&other) = default;
0176    RProxiedCollectionField &operator=(RProxiedCollectionField &&other) = default;
0177    ~RProxiedCollectionField() override = default;
0178 
0179    std::vector<RValue> SplitValue(const RValue &value) const final;
0180    size_t GetValueSize() const final { return fProxy->Sizeof(); }
0181    size_t GetAlignment() const final { return alignof(std::max_align_t); }
0182    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0183    void
0184    GetCollectionInfo(ROOT::NTupleSize_t globalIndex, RNTupleLocalIndex *collectionStart, ROOT::NTupleSize_t *size) const
0185    {
0186       fPrincipalColumn->GetCollectionInfo(globalIndex, collectionStart, size);
0187    }
0188    void
0189    GetCollectionInfo(RNTupleLocalIndex localIndex, RNTupleLocalIndex *collectionStart, ROOT::NTupleSize_t *size) const
0190    {
0191       fPrincipalColumn->GetCollectionInfo(localIndex, collectionStart, size);
0192    }
0193 };
0194 
0195 ////////////////////////////////////////////////////////////////////////////////
0196 /// Template specializations for classes with collection proxies
0197 ////////////////////////////////////////////////////////////////////////////////
0198 
0199 template <typename T, typename = void>
0200 struct HasCollectionProxyMemberType : std::false_type {
0201 };
0202 template <typename T>
0203 struct HasCollectionProxyMemberType<
0204    T, typename std::enable_if<std::is_same<typename T::IsCollectionProxy, std::true_type>::value>::type>
0205    : std::true_type {
0206 };
0207 
0208 /* The point here is that we can only tell at run time if a class has an associated collection proxy.
0209 For compile time, in the first iteration of this PR we had an extra template argument that acted as a "tag" to
0210 differentiate the RField specialization for classes with an associated collection proxy (inherits
0211 RProxiedCollectionField) from the RField primary template definition (RClassField-derived), as in:
0212 ```
0213 auto field = std::make_unique<RField<MyClass>>("klass");
0214 // vs
0215 auto otherField = std::make_unique<RField<MyClass, ROOT::Experimental::TagIsCollectionProxy>>("klass");
0216 ```
0217 
0218 That is convenient only for non-nested types, i.e. it doesn't work with, e.g. `RField<std::vector<MyClass>,
0219 ROOT::Experimental::TagIsCollectionProxy>`, as the tag is not forwarded to the instantiation of the inner RField
0220 (that for the value type of the vector).  The following two possible solutions were considered:
0221 - A wrapper type that helps to differentiate both cases.
0222 There we would have:
0223 ```
0224 auto field = std::make_unique<RField<RProxiedCollection<MyClass>>>("klass"); // Using collection proxy
0225 ```
0226 - A helper IsCollectionProxy<T> type, that can be used in a similar way to those in the `<type_traits>` header.
0227 We found this more convenient and is the implemented thing below.  Here, classes can be marked as a
0228 collection proxy with either of the following two forms (whichever is more convenient for the user):
0229 ```
0230 template <>
0231 struct IsCollectionProxy<MyClass> : std::true_type {};
0232 ```
0233 or by adding a member type to the class as follows:
0234 ```
0235 class MyClass {
0236 public:
0237    using IsCollectionProxy = std::true_type;
0238 };
0239 ```
0240 
0241 Of course, there is another possible solution which is to have a single RClassField that implements both
0242 the regular-class and the collection-proxy behaviors, and always chooses appropriately at run time.
0243 We found that less clean and probably has more overhead, as most probably it involves an additional branch + call
0244 in each of the member functions. */
0245 /// Helper type trait for marking classes as a collection proxy.
0246 /// This type trait must be set for collection proxy-based RNTuple fields created through MakeField<T>.
0247 template <typename T, typename = void>
0248 struct IsCollectionProxy : HasCollectionProxyMemberType<T> {
0249 };
0250 
0251 /// Classes behaving as a collection of elements that can be queried via the TVirtualCollectionProxy interface
0252 /// The use of a collection proxy for a particular class can be enabled via:
0253 /// ```
0254 /// namespace ROOT::Experimental {
0255 ///    template <> struct IsCollectionProxy<Classname> : std::true_type {};
0256 /// }
0257 /// ```
0258 /// Alternatively, this can be achieved by adding a member type to the class definition as follows:
0259 /// ```
0260 /// class Classname {
0261 /// public:
0262 ///    using IsCollectionProxy = std::true_type;
0263 /// };
0264 /// ```
0265 template <typename T>
0266 class RField<T, typename std::enable_if<IsCollectionProxy<T>::value>::type> final : public RProxiedCollectionField {
0267 public:
0268    static std::string TypeName() { return ROOT::Internal::GetRenormalizedDemangledTypeName(typeid(T)); }
0269    RField(std::string_view name) : RProxiedCollectionField(name, TypeName())
0270    {
0271       static_assert(std::is_class<T>::value, "collection proxy unsupported for fundamental types");
0272    }
0273    RField(RField &&other) = default;
0274    RField &operator=(RField &&other) = default;
0275    ~RField() final = default;
0276 };
0277 
0278 ////////////////////////////////////////////////////////////////////////////////
0279 /// Template specializations for C++ std::[unordered_][multi]map
0280 ////////////////////////////////////////////////////////////////////////////////
0281 
0282 /// The generic field for a `std::map<KeyType, ValueType>` and `std::unordered_map<KeyType, ValueType>`
0283 class RMapField : public RProxiedCollectionField {
0284 public:
0285    RMapField(std::string_view fieldName, std::string_view typeName, std::unique_ptr<RFieldBase> itemField);
0286    RMapField(RMapField &&other) = default;
0287    RMapField &operator=(RMapField &&other) = default;
0288    ~RMapField() override = default;
0289 };
0290 
0291 template <typename KeyT, typename ValueT>
0292 class RField<std::map<KeyT, ValueT>> final : public RMapField {
0293 public:
0294    static std::string TypeName()
0295    {
0296       return "std::map<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
0297    }
0298 
0299    explicit RField(std::string_view name)
0300       : RMapField(name, TypeName(), std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
0301    {
0302    }
0303    RField(RField &&other) = default;
0304    RField &operator=(RField &&other) = default;
0305    ~RField() final = default;
0306 };
0307 
0308 template <typename KeyT, typename ValueT>
0309 class RField<std::unordered_map<KeyT, ValueT>> final : public RMapField {
0310 public:
0311    static std::string TypeName()
0312    {
0313       return "std::unordered_map<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
0314    }
0315 
0316    explicit RField(std::string_view name)
0317       : RMapField(name, TypeName(), std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
0318    {
0319    }
0320    RField(RField &&other) = default;
0321    RField &operator=(RField &&other) = default;
0322    ~RField() final = default;
0323 };
0324 
0325 template <typename KeyT, typename ValueT>
0326 class RField<std::multimap<KeyT, ValueT>> final : public RMapField {
0327 public:
0328    static std::string TypeName()
0329    {
0330       return "std::multimap<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
0331    }
0332 
0333    explicit RField(std::string_view name)
0334       : RMapField(name, TypeName(), std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
0335    {
0336    }
0337    RField(RField &&other) = default;
0338    RField &operator=(RField &&other) = default;
0339    ~RField() final = default;
0340 };
0341 
0342 template <typename KeyT, typename ValueT>
0343 class RField<std::unordered_multimap<KeyT, ValueT>> final : public RMapField {
0344 public:
0345    static std::string TypeName()
0346    {
0347       return "std::unordered_multimap<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
0348    }
0349 
0350    explicit RField(std::string_view name)
0351       : RMapField(name, TypeName(), std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
0352    {
0353    }
0354    RField(RField &&other) = default;
0355    RField &operator=(RField &&other) = default;
0356    ~RField() final = default;
0357 };
0358 
0359 ////////////////////////////////////////////////////////////////////////////////
0360 /// Template specializations for C++ std::[unordered_][multi]set
0361 ////////////////////////////////////////////////////////////////////////////////
0362 
0363 /// The generic field for a `std::set<Type>` and `std::unordered_set<Type>`
0364 class RSetField : public RProxiedCollectionField {
0365 public:
0366    RSetField(std::string_view fieldName, std::string_view typeName, std::unique_ptr<RFieldBase> itemField);
0367    RSetField(RSetField &&other) = default;
0368    RSetField &operator=(RSetField &&other) = default;
0369    ~RSetField() override = default;
0370 };
0371 
0372 template <typename ItemT>
0373 class RField<std::set<ItemT>> final : public RSetField {
0374 public:
0375    static std::string TypeName() { return "std::set<" + RField<ItemT>::TypeName() + ">"; }
0376 
0377    explicit RField(std::string_view name) : RSetField(name, TypeName(), std::make_unique<RField<ItemT>>("_0")) {}
0378    RField(RField &&other) = default;
0379    RField &operator=(RField &&other) = default;
0380    ~RField() final = default;
0381 };
0382 
0383 template <typename ItemT>
0384 class RField<std::unordered_set<ItemT>> final : public RSetField {
0385 public:
0386    static std::string TypeName() { return "std::unordered_set<" + RField<ItemT>::TypeName() + ">"; }
0387 
0388    explicit RField(std::string_view name) : RSetField(name, TypeName(), std::make_unique<RField<ItemT>>("_0")) {}
0389    RField(RField &&other) = default;
0390    RField &operator=(RField &&other) = default;
0391    ~RField() final = default;
0392 };
0393 
0394 template <typename ItemT>
0395 class RField<std::multiset<ItemT>> final : public RSetField {
0396 public:
0397    static std::string TypeName() { return "std::multiset<" + RField<ItemT>::TypeName() + ">"; }
0398 
0399    explicit RField(std::string_view name) : RSetField(name, TypeName(), std::make_unique<RField<ItemT>>("_0")) {}
0400    RField(RField &&other) = default;
0401    RField &operator=(RField &&other) = default;
0402    ~RField() final = default;
0403 };
0404 
0405 template <typename ItemT>
0406 class RField<std::unordered_multiset<ItemT>> final : public RSetField {
0407 public:
0408    static std::string TypeName() { return "std::unordered_multiset<" + RField<ItemT>::TypeName() + ">"; }
0409 
0410    explicit RField(std::string_view name) : RSetField(name, TypeName(), std::make_unique<RField<ItemT>>("_0")) {}
0411    RField(RField &&other) = default;
0412    RField &operator=(RField &&other) = default;
0413    ~RField() final = default;
0414 };
0415 
0416 } // namespace ROOT
0417 
0418 #endif