Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-15 10:28:35

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/RNTupleTypes.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. Note that this constructor requires manual initialization of the item field
0154    /// (Attach() and setting fItemSize)
0155    RProxiedCollectionField(std::string_view fieldName, TClass *classp);
0156 
0157    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const override;
0158    const RColumnRepresentations &GetColumnRepresentations() const final;
0159    void GenerateColumns() final;
0160    void GenerateColumns(const ROOT::RNTupleDescriptor &desc) final;
0161 
0162    void ConstructValue(void *where) const final;
0163    std::unique_ptr<RDeleter> GetDeleter() const final;
0164 
0165    std::size_t AppendImpl(const void *from) final;
0166    void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
0167 
0168    void ReconcileOnDiskField(const RNTupleDescriptor &desc) override;
0169 
0170    void CommitClusterImpl() final { fNWritten = 0; }
0171 
0172 public:
0173    RProxiedCollectionField(std::string_view fieldName, std::string_view typeName);
0174    RProxiedCollectionField(RProxiedCollectionField &&other) = default;
0175    RProxiedCollectionField &operator=(RProxiedCollectionField &&other) = default;
0176    ~RProxiedCollectionField() override = default;
0177 
0178    std::vector<RValue> SplitValue(const RValue &value) const final;
0179    size_t GetValueSize() const final { return fProxy->Sizeof(); }
0180    size_t GetAlignment() const final { return alignof(std::max_align_t); }
0181    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0182 };
0183 
0184 ////////////////////////////////////////////////////////////////////////////////
0185 /// Template specializations for classes with collection proxies
0186 ////////////////////////////////////////////////////////////////////////////////
0187 
0188 template <typename T, typename = void>
0189 struct HasCollectionProxyMemberType : std::false_type {
0190 };
0191 template <typename T>
0192 struct HasCollectionProxyMemberType<
0193    T, typename std::enable_if<std::is_same<typename T::IsCollectionProxy, std::true_type>::value>::type>
0194    : std::true_type {
0195 };
0196 
0197 /* The point here is that we can only tell at run time if a class has an associated collection proxy.
0198 For compile time, in the first iteration of this PR we had an extra template argument that acted as a "tag" to
0199 differentiate the RField specialization for classes with an associated collection proxy (inherits
0200 RProxiedCollectionField) from the RField primary template definition (RClassField-derived), as in:
0201 ```
0202 auto field = std::make_unique<RField<MyClass>>("klass");
0203 // vs
0204 auto otherField = std::make_unique<RField<MyClass, ROOT::TagIsCollectionProxy>>("klass");
0205 ```
0206 
0207 That is convenient only for non-nested types, i.e. it doesn't work with, e.g. `RField<std::vector<MyClass>,
0208 ROOT::TagIsCollectionProxy>`, as the tag is not forwarded to the instantiation of the inner RField
0209 (that for the value type of the vector).  The following two possible solutions were considered:
0210 - A wrapper type that helps to differentiate both cases.
0211 There we would have:
0212 ```
0213 auto field = std::make_unique<RField<RProxiedCollection<MyClass>>>("klass"); // Using collection proxy
0214 ```
0215 - A helper IsCollectionProxy<T> type, that can be used in a similar way to those in the `<type_traits>` header.
0216 We found this more convenient and is the implemented thing below.  Here, classes can be marked as a
0217 collection proxy with either of the following two forms (whichever is more convenient for the user):
0218 ```
0219 template <>
0220 struct IsCollectionProxy<MyClass> : std::true_type {};
0221 ```
0222 or by adding a member type to the class as follows:
0223 ```
0224 class MyClass {
0225 public:
0226    using IsCollectionProxy = std::true_type;
0227 };
0228 ```
0229 
0230 Of course, there is another possible solution which is to have a single RClassField that implements both
0231 the regular-class and the collection-proxy behaviors, and always chooses appropriately at run time.
0232 We found that less clean and probably has more overhead, as most probably it involves an additional branch + call
0233 in each of the member functions. */
0234 /// Helper type trait for marking classes as a collection proxy.
0235 /// This type trait must be set for collection proxy-based RNTuple fields created through MakeField<T>.
0236 template <typename T, typename = void>
0237 struct IsCollectionProxy : HasCollectionProxyMemberType<T> {
0238 };
0239 
0240 /// Classes behaving as a collection of elements that can be queried via the TVirtualCollectionProxy interface
0241 /// The use of a collection proxy for a particular class can be enabled via:
0242 /// ```
0243 /// namespace ROOT {
0244 ///    template <> struct IsCollectionProxy<Classname> : std::true_type {};
0245 /// }
0246 /// ```
0247 /// Alternatively, this can be achieved by adding a member type to the class definition as follows:
0248 /// ```
0249 /// class Classname {
0250 /// public:
0251 ///    using IsCollectionProxy = std::true_type;
0252 /// };
0253 /// ```
0254 template <typename T>
0255 class RField<T, typename std::enable_if<IsCollectionProxy<T>::value>::type> final : public RProxiedCollectionField {
0256 public:
0257    static std::string TypeName() { return ROOT::Internal::GetRenormalizedTypeName(typeid(T)); }
0258    RField(std::string_view name) : RProxiedCollectionField(name, TypeName())
0259    {
0260       static_assert(std::is_class<T>::value, "collection proxy unsupported for fundamental types");
0261    }
0262    RField(RField &&other) = default;
0263    RField &operator=(RField &&other) = default;
0264    ~RField() final = default;
0265 };
0266 
0267 ////////////////////////////////////////////////////////////////////////////////
0268 /// Template specializations for C++ std::[unordered_][multi]map
0269 ////////////////////////////////////////////////////////////////////////////////
0270 
0271 /// The generic field for a `std::map<KeyType, ValueType>` and `std::unordered_map<KeyType, ValueType>`
0272 class RMapField : public RProxiedCollectionField {
0273 public:
0274    enum class EMapType {
0275       kMap,
0276       kUnorderedMap,
0277       kMultiMap,
0278       kUnorderedMultiMap
0279    };
0280 
0281 private:
0282    EMapType fMapType;
0283 
0284 protected:
0285    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0286    void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;
0287 
0288 public:
0289    RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField);
0290    RMapField(RMapField &&other) = default;
0291    RMapField &operator=(RMapField &&other) = default;
0292    ~RMapField() override = default;
0293 };
0294 
0295 template <typename KeyT, typename ValueT>
0296 class RField<std::map<KeyT, ValueT>> final : public RMapField {
0297 public:
0298    static std::string TypeName()
0299    {
0300       return "std::map<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
0301    }
0302 
0303    explicit RField(std::string_view name)
0304       : RMapField(name, EMapType::kMap, std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
0305    {
0306    }
0307    RField(RField &&other) = default;
0308    RField &operator=(RField &&other) = default;
0309    ~RField() final = default;
0310 };
0311 
0312 template <typename KeyT, typename ValueT>
0313 class RField<std::unordered_map<KeyT, ValueT>> final : public RMapField {
0314 public:
0315    static std::string TypeName()
0316    {
0317       return "std::unordered_map<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
0318    }
0319 
0320    explicit RField(std::string_view name)
0321       : RMapField(name, EMapType::kUnorderedMap, std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
0322    {
0323    }
0324    RField(RField &&other) = default;
0325    RField &operator=(RField &&other) = default;
0326    ~RField() final = default;
0327 };
0328 
0329 template <typename KeyT, typename ValueT>
0330 class RField<std::multimap<KeyT, ValueT>> final : public RMapField {
0331 public:
0332    static std::string TypeName()
0333    {
0334       return "std::multimap<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
0335    }
0336 
0337    explicit RField(std::string_view name)
0338       : RMapField(name, EMapType::kMultiMap, std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
0339    {
0340    }
0341    RField(RField &&other) = default;
0342    RField &operator=(RField &&other) = default;
0343    ~RField() final = default;
0344 };
0345 
0346 template <typename KeyT, typename ValueT>
0347 class RField<std::unordered_multimap<KeyT, ValueT>> final : public RMapField {
0348 public:
0349    static std::string TypeName()
0350    {
0351       return "std::unordered_multimap<" + RField<KeyT>::TypeName() + "," + RField<ValueT>::TypeName() + ">";
0352    }
0353 
0354    explicit RField(std::string_view name)
0355       : RMapField(name, EMapType::kUnorderedMultiMap, std::make_unique<RField<std::pair<KeyT, ValueT>>>("_0"))
0356    {
0357    }
0358    RField(RField &&other) = default;
0359    RField &operator=(RField &&other) = default;
0360    ~RField() final = default;
0361 };
0362 
0363 ////////////////////////////////////////////////////////////////////////////////
0364 /// Template specializations for C++ std::[unordered_][multi]set
0365 ////////////////////////////////////////////////////////////////////////////////
0366 
0367 /// The generic field for a `std::set<Type>` and `std::unordered_set<Type>`
0368 class RSetField : public RProxiedCollectionField {
0369 public:
0370    enum class ESetType {
0371       kSet,
0372       kUnorderedSet,
0373       kMultiSet,
0374       kUnorderedMultiSet
0375    };
0376 
0377 private:
0378    ESetType fSetType;
0379 
0380 protected:
0381    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0382    void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;
0383 
0384 public:
0385    RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField);
0386    RSetField(RSetField &&other) = default;
0387    RSetField &operator=(RSetField &&other) = default;
0388    ~RSetField() override = default;
0389 };
0390 
0391 template <typename ItemT>
0392 class RField<std::set<ItemT>> final : public RSetField {
0393 public:
0394    static std::string TypeName() { return "std::set<" + RField<ItemT>::TypeName() + ">"; }
0395 
0396    explicit RField(std::string_view name) : RSetField(name, ESetType::kSet, std::make_unique<RField<ItemT>>("_0")) {}
0397    RField(RField &&other) = default;
0398    RField &operator=(RField &&other) = default;
0399    ~RField() final = default;
0400 };
0401 
0402 template <typename ItemT>
0403 class RField<std::unordered_set<ItemT>> final : public RSetField {
0404 public:
0405    static std::string TypeName() { return "std::unordered_set<" + RField<ItemT>::TypeName() + ">"; }
0406 
0407    explicit RField(std::string_view name)
0408       : RSetField(name, ESetType::kUnorderedSet, std::make_unique<RField<ItemT>>("_0"))
0409    {
0410    }
0411    RField(RField &&other) = default;
0412    RField &operator=(RField &&other) = default;
0413    ~RField() final = default;
0414 };
0415 
0416 template <typename ItemT>
0417 class RField<std::multiset<ItemT>> final : public RSetField {
0418 public:
0419    static std::string TypeName() { return "std::multiset<" + RField<ItemT>::TypeName() + ">"; }
0420 
0421    explicit RField(std::string_view name) : RSetField(name, ESetType::kMultiSet, std::make_unique<RField<ItemT>>("_0"))
0422    {
0423    }
0424    RField(RField &&other) = default;
0425    RField &operator=(RField &&other) = default;
0426    ~RField() final = default;
0427 };
0428 
0429 template <typename ItemT>
0430 class RField<std::unordered_multiset<ItemT>> final : public RSetField {
0431 public:
0432    static std::string TypeName() { return "std::unordered_multiset<" + RField<ItemT>::TypeName() + ">"; }
0433 
0434    explicit RField(std::string_view name)
0435       : RSetField(name, ESetType::kUnorderedMultiSet, std::make_unique<RField<ItemT>>("_0"))
0436    {
0437    }
0438    RField(RField &&other) = default;
0439    RField &operator=(RField &&other) = default;
0440    ~RField() final = default;
0441 };
0442 
0443 } // namespace ROOT
0444 
0445 #endif