Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-21 09:23:46

0001 /// \file ROOT/RField.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
0015 #define ROOT_RField
0016 
0017 #include <ROOT/RError.hxx>
0018 #include <ROOT/RFieldBase.hxx>
0019 #include <ROOT/RFieldUtils.hxx>
0020 #include <ROOT/RNTupleSerialize.hxx>
0021 #include <ROOT/RNTupleTypes.hxx>
0022 #include <ROOT/RSpan.hxx>
0023 #include <string_view>
0024 #include <ROOT/TypeTraits.hxx>
0025 
0026 #include <TGenericClassInfo.h>
0027 
0028 #include <algorithm>
0029 #include <array>
0030 #include <cstddef>
0031 #include <iostream>
0032 #include <memory>
0033 #include <string>
0034 #include <type_traits>
0035 #include <typeinfo>
0036 #include <vector>
0037 
0038 class TClass;
0039 class TEnum;
0040 class TObject;
0041 class TVirtualStreamerInfo;
0042 
0043 namespace ROOT {
0044 
0045 class TSchemaRule;
0046 class RNTupleCollectionView;
0047 
0048 namespace Detail {
0049 class RFieldVisitor;
0050 } // namespace Detail
0051 
0052 class RFieldZero;
0053 namespace Internal {
0054 void SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val);
0055 }
0056 
0057 /// The container field for an ntuple model, which itself has no physical representation.
0058 /// Therefore, the zero field must not be connected to a page source or sink.
0059 class RFieldZero final : public RFieldBase {
0060    friend void ROOT::Internal::SetAllowFieldSubstitutions(RFieldZero &, bool);
0061 
0062    /// If field substitutions are allowed, upon connecting to a page source the field hierarchy will replace created
0063    /// fields by fields that match the on-disk schema. This happens for
0064    ///     - Vector fields (RVectorField, RRVecField) that connect to an on-disk fixed-size array
0065    ///     - Streamer fields that connect to an on-disk class field
0066    /// Field substitutions must not be enabled when the field hierarchy already handed out RValue objects because
0067    /// they would leave dangling field pointers to the replaced fields. This is used in cases when the field/model
0068    /// is created by RNTuple (not imposed), before it is made available to the user.
0069    /// This flag is reset on Clone().
0070    bool fAllowFieldSubstitutions = false;
0071 
0072    std::unordered_set<std::string> fSubfieldNames; ///< Efficient detection of duplicate field names
0073 
0074 protected:
0075    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0076    void ConstructValue(void *) const final {}
0077 
0078 public:
0079    RFieldZero();
0080 
0081    /// A public version of the Attach method that allows piece-wise construction of the zero field.
0082    /// Will throw on duplicate subfield names.
0083    void Attach(std::unique_ptr<RFieldBase> child);
0084    size_t GetValueSize() const final { return 0; }
0085    size_t GetAlignment() const final { return 0; }
0086 
0087    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0088 
0089    bool GetAllowFieldSubstitutions() const { return fAllowFieldSubstitutions; }
0090    /// Moves all subfields into the returned vector.
0091    std::vector<std::unique_ptr<RFieldBase>> ReleaseSubfields();
0092 };
0093 
0094 /// Used in RFieldBase::Check() to record field creation failures.
0095 /// Also used when deserializing a field that contains unknown values that may come from
0096 /// future RNTuple versions (e.g. an unknown Structure)
0097 class RInvalidField final : public RFieldBase {
0098 public:
0099    enum class ECategory {
0100       /// Generic unrecoverable error
0101       kGeneric,
0102       /// The type given to RFieldBase::Create was invalid
0103       kTypeError,
0104       /// The type given to RFieldBase::Create was unknown
0105       kUnknownType,
0106       /// The field could not be created because its descriptor had an unknown structural role
0107       kUnknownStructure,
0108    };
0109 
0110    using RCategory R__DEPRECATED(6, 42, "enum renamed to ECategory") = ECategory;
0111 
0112 private:
0113    std::string fError;
0114    ECategory fCategory;
0115 
0116 protected:
0117    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
0118    {
0119       return std::make_unique<RInvalidField>(newName, GetTypeName(), fError, fCategory);
0120    }
0121    void ConstructValue(void *) const final {}
0122 
0123 public:
0124    RInvalidField(std::string_view name, std::string_view type, std::string_view error, ECategory category)
0125       : RFieldBase(name, type, ROOT::ENTupleStructure::kPlain, false /* isSimple */), fError(error), fCategory(category)
0126    {
0127       fTraits |= kTraitInvalidField;
0128    }
0129 
0130    const std::string &GetError() const { return fError; }
0131    ECategory GetCategory() const { return fCategory; }
0132 
0133    size_t GetValueSize() const final { return 0; }
0134    size_t GetAlignment() const final { return 0; }
0135 }; // RInvalidField
0136 
0137 /// The field for a class with dictionary
0138 class RClassField : public RFieldBase {
0139 private:
0140    enum ESubfieldRole {
0141       kBaseClass,
0142       kDataMember,
0143    };
0144    struct RSubfieldInfo {
0145       ESubfieldRole fRole;
0146       std::size_t fOffset;
0147    };
0148    // Information to read into the staging area a field that is used as an input to an I/O customization rule
0149    struct RStagingItem {
0150       /// The field used to read the on-disk data. The fields type may be different from the on-disk type as long
0151       /// as the on-disk type can be converted to the fields type (through type cast / schema evolution).
0152       std::unique_ptr<RFieldBase> fField;
0153       std::size_t fOffset; ///< offset in fStagingArea
0154    };
0155    /// Prefix used in the subfield names generated for base classes
0156    static constexpr const char *kPrefixInherited{":"};
0157 
0158    class RClassDeleter : public RDeleter {
0159    private:
0160       TClass *fClass;
0161 
0162    public:
0163       explicit RClassDeleter(TClass *cl) : fClass(cl) {}
0164       void operator()(void *objPtr, bool dtorOnly) final;
0165    };
0166 
0167    TClass *fClass;
0168    /// Additional information kept for each entry in `fSubfields`
0169    std::vector<RSubfieldInfo> fSubfieldsInfo;
0170    std::size_t fMaxAlignment = 1;
0171 
0172    /// The staging area stores inputs to I/O rules according to the offsets given by the streamer info of
0173    /// "TypeName@@Version". The area is allocated depending on I/O rules resp. the source members of the I/O rules.
0174    std::unique_ptr<unsigned char[]> fStagingArea;
0175    /// The TClass instance that corresponds to the staging area.
0176    /// The staging class exists as <class name>@@<on-disk version> if the on-disk version is different from the
0177    /// current in-memory version, or it can be accessed by the first @@alloc streamer element of the current streamer
0178    /// info.
0179    TClass *fStagingClass = nullptr;
0180    std::unordered_map<std::string, RStagingItem> fStagingItems; ///< Lookup staging items by member name
0181 
0182 private:
0183    RClassField(std::string_view fieldName, const RClassField &source); ///< Used by CloneImpl
0184    RClassField(std::string_view fieldName, TClass *classp);
0185    void Attach(std::unique_ptr<RFieldBase> child, RSubfieldInfo info);
0186 
0187    /// Returns the id of member 'name' in the class field given by 'fieldId', or kInvalidDescriptorId if no such
0188    /// member exist. Looks recursively in base classes.
0189    ROOT::DescriptorId_t
0190    LookupMember(const ROOT::RNTupleDescriptor &desc, std::string_view memberName, ROOT::DescriptorId_t classFieldId);
0191    /// Sets fStagingClass according to the given name and version
0192    void SetStagingClass(const std::string &className, unsigned int classVersion);
0193    /// If there are rules with inputs (source members), create the staging area according to the TClass instance
0194    /// that corresponds to the on-disk field.
0195    void PrepareStagingArea(const std::vector<const TSchemaRule *> &rules, const ROOT::RNTupleDescriptor &desc,
0196                            const ROOT::RFieldDescriptor &classFieldId);
0197    /// Register post-read callback corresponding to a ROOT I/O customization rules.
0198    void AddReadCallbacksFromIORule(const TSchemaRule *rule);
0199    /// Given the on-disk information from the page source, find all the I/O customization rules that apply
0200    /// to the class field at hand, to which the fieldDesc descriptor, if provided, must correspond.
0201    /// Fields may not have an on-disk representation (e.g., when inserted by schema evolution), in which case the passed
0202    /// field descriptor is nullptr.
0203    std::vector<const TSchemaRule *> FindRules(const ROOT::RFieldDescriptor *fieldDesc);
0204 
0205 protected:
0206    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0207 
0208    void ConstructValue(void *where) const final;
0209    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RClassDeleter>(fClass); }
0210 
0211    std::size_t AppendImpl(const void *from) final;
0212    void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
0213    void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final;
0214 
0215    std::unique_ptr<RFieldBase> BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final;
0216    void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;
0217 
0218 public:
0219    RClassField(std::string_view fieldName, std::string_view className);
0220    RClassField(RClassField &&other) = default;
0221    RClassField &operator=(RClassField &&other) = default;
0222    ~RClassField() override;
0223 
0224    std::vector<RValue> SplitValue(const RValue &value) const final;
0225    size_t GetValueSize() const final;
0226    size_t GetAlignment() const final { return fMaxAlignment; }
0227    std::uint32_t GetTypeVersion() const final;
0228    std::uint32_t GetTypeChecksum() const final;
0229    /// For polymorphic classes (that declare or inherit at least one virtual method), return the expected dynamic type
0230    /// of any user object. If the class is not polymorphic, return nullptr.
0231    const std::type_info *GetPolymorphicTypeInfo() const;
0232    /// Return the TClass instance backing this field.
0233    const TClass *GetClass() const { return fClass; }
0234    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0235 };
0236 
0237 /// The field for a class using ROOT standard streaming
0238 class RStreamerField final : public RFieldBase {
0239 private:
0240    class RStreamerFieldDeleter : public RDeleter {
0241    private:
0242       TClass *fClass;
0243 
0244    public:
0245       explicit RStreamerFieldDeleter(TClass *cl) : fClass(cl) {}
0246       void operator()(void *objPtr, bool dtorOnly) final;
0247    };
0248 
0249    TClass *fClass = nullptr;
0250    ROOT::Internal::RNTupleSerializer::StreamerInfoMap_t fStreamerInfos; ///< streamer info records seen during writing
0251    ROOT::Internal::RColumnIndex fIndex; ///< number of bytes written in the current cluster
0252 
0253 private:
0254    RStreamerField(std::string_view fieldName, TClass *classp);
0255 
0256 protected:
0257    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0258 
0259    const RColumnRepresentations &GetColumnRepresentations() const final;
0260    void GenerateColumns() final;
0261    void GenerateColumns(const ROOT::RNTupleDescriptor &) final;
0262 
0263    void ConstructValue(void *where) const final;
0264    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RStreamerFieldDeleter>(fClass); }
0265 
0266    std::size_t AppendImpl(const void *from) final;
0267    void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
0268 
0269    void CommitClusterImpl() final { fIndex = 0; }
0270 
0271    bool HasExtraTypeInfo() const final { return true; }
0272    // Returns the list of seen streamer infos
0273    ROOT::RExtraTypeInfoDescriptor GetExtraTypeInfo() const final;
0274 
0275    std::unique_ptr<RFieldBase> BeforeConnectPageSource(ROOT::Internal::RPageSource &source) final;
0276    void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;
0277 
0278 public:
0279    RStreamerField(std::string_view fieldName, std::string_view className);
0280    RStreamerField(RStreamerField &&other) = default;
0281    RStreamerField &operator=(RStreamerField &&other) = default;
0282    ~RStreamerField() final = default;
0283 
0284    size_t GetValueSize() const final;
0285    size_t GetAlignment() const final;
0286    std::uint32_t GetTypeVersion() const final;
0287    std::uint32_t GetTypeChecksum() const final;
0288    TClass *GetClass() const { return fClass; }
0289    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0290 };
0291 
0292 /// The field for an unscoped or scoped enum with dictionary
0293 class REnumField : public RFieldBase {
0294 private:
0295    REnumField(std::string_view fieldName, TEnum *enump);
0296    REnumField(std::string_view fieldName, std::string_view enumName, std::unique_ptr<RFieldBase> intField);
0297 
0298 protected:
0299    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0300 
0301    void ConstructValue(void *where) const final { CallConstructValueOn(*fSubfields[0], where); }
0302 
0303    std::size_t AppendImpl(const void *from) final { return CallAppendOn(*fSubfields[0], from); }
0304    void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final { CallReadOn(*fSubfields[0], globalIndex, to); }
0305    void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final { CallReadOn(*fSubfields[0], localIndex, to); }
0306 
0307    void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;
0308 
0309 public:
0310    REnumField(std::string_view fieldName, std::string_view enumName);
0311    REnumField(REnumField &&other) = default;
0312    REnumField &operator=(REnumField &&other) = default;
0313    ~REnumField() override = default;
0314 
0315    std::vector<RValue> SplitValue(const RValue &value) const final;
0316    size_t GetValueSize() const final { return fSubfields[0]->GetValueSize(); }
0317    size_t GetAlignment() const final { return fSubfields[0]->GetAlignment(); }
0318    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0319 };
0320 
0321 /// Classes with dictionaries that can be inspected by TClass
0322 template <typename T, typename = void>
0323 class RField final : public RClassField {
0324 public:
0325    static std::string TypeName() { return ROOT::Internal::GetRenormalizedTypeName(typeid(T)); }
0326    RField(std::string_view name) : RClassField(name, Internal::GetDemangledTypeName(typeid(T)))
0327    {
0328       static_assert(std::is_class_v<T>, "no I/O support for this basic C++ type");
0329    }
0330    RField(RField &&other) = default;
0331    RField &operator=(RField &&other) = default;
0332    ~RField() final = default;
0333 };
0334 
0335 template <typename T>
0336 class RField<T, typename std::enable_if<std::is_enum_v<T>>::type> final : public REnumField {
0337 public:
0338    static std::string TypeName() { return ROOT::Internal::GetRenormalizedTypeName(typeid(T)); }
0339    RField(std::string_view name) : REnumField(name, TypeName()) {}
0340    RField(RField &&other) = default;
0341    RField &operator=(RField &&other) = default;
0342    ~RField() final = default;
0343 };
0344 
0345 /// An artificial field that transforms an RNTuple column that contains the offset of collections into
0346 /// collection sizes. It is only used for reading, e.g. as projected field or as an artificial field that provides the
0347 /// "number of" RDF columns for collections (e.g. `R_rdf_sizeof_jets` for a collection named `jets`).
0348 /// It is used in the templated RField<RNTupleCardinality<SizeT>> form, which represents the collection sizes either
0349 /// as 32bit unsigned int (std::uint32_t) or as 64bit unsigned int (std::uint64_t).
0350 class RCardinalityField : public RFieldBase {
0351    friend class ROOT::RNTupleCollectionView; // to access GetCollectionInfo()
0352 
0353 private:
0354    void GetCollectionInfo(ROOT::NTupleSize_t globalIndex, RNTupleLocalIndex *collectionStart, ROOT::NTupleSize_t *size)
0355    {
0356       fPrincipalColumn->GetCollectionInfo(globalIndex, collectionStart, size);
0357    }
0358    void GetCollectionInfo(RNTupleLocalIndex localIndex, RNTupleLocalIndex *collectionStart, ROOT::NTupleSize_t *size)
0359    {
0360       fPrincipalColumn->GetCollectionInfo(localIndex, collectionStart, size);
0361    }
0362 
0363 protected:
0364    RCardinalityField(std::string_view fieldName, std::string_view typeName)
0365       : RFieldBase(fieldName, typeName, ROOT::ENTupleStructure::kPlain, false /* isSimple */)
0366    {
0367    }
0368 
0369    const RColumnRepresentations &GetColumnRepresentations() const final;
0370    // Field is only used for reading
0371    void GenerateColumns() final { throw RException(R__FAIL("Cardinality fields must only be used for reading")); }
0372    void GenerateColumns(const ROOT::RNTupleDescriptor &) final;
0373 
0374    void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;
0375 
0376 public:
0377    RCardinalityField(RCardinalityField &&other) = default;
0378    RCardinalityField &operator=(RCardinalityField &&other) = default;
0379    ~RCardinalityField() override = default;
0380 
0381    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0382 
0383    const RField<RNTupleCardinality<std::uint32_t>> *As32Bit() const;
0384    const RField<RNTupleCardinality<std::uint64_t>> *As64Bit() const;
0385 };
0386 
0387 template <typename T>
0388 class RSimpleField : public RFieldBase {
0389    void ReconcileIntegralField(const RNTupleDescriptor &desc);
0390    void ReconcileFloatingPointField(const RNTupleDescriptor &desc);
0391 
0392 protected:
0393    void GenerateColumns() override { GenerateColumnsImpl<T>(); }
0394    void GenerateColumns(const ROOT::RNTupleDescriptor &desc) override { GenerateColumnsImpl<T>(desc); }
0395 
0396    void ConstructValue(void *where) const final { new (where) T{0}; }
0397 
0398    void ReconcileOnDiskField(const RNTupleDescriptor &desc) override
0399    {
0400       if constexpr (std::is_integral_v<T>) {
0401          ReconcileIntegralField(desc);
0402       } else if constexpr (std::is_floating_point_v<T>) {
0403          ReconcileFloatingPointField(desc);
0404       } else {
0405          RFieldBase::ReconcileOnDiskField(desc);
0406       }
0407    }
0408 
0409    RSimpleField(std::string_view name, std::string_view type)
0410       : RFieldBase(name, type, ROOT::ENTupleStructure::kPlain, true /* isSimple */)
0411    {
0412       fTraits |= kTraitTrivialType;
0413    }
0414 
0415 public:
0416    RSimpleField(RSimpleField &&other) = default;
0417    RSimpleField &operator=(RSimpleField &&other) = default;
0418    ~RSimpleField() override = default;
0419 
0420    T *Map(ROOT::NTupleSize_t globalIndex) { return fPrincipalColumn->Map<T>(globalIndex); }
0421    T *Map(RNTupleLocalIndex localIndex) { return fPrincipalColumn->Map<T>(localIndex); }
0422    T *MapV(ROOT::NTupleSize_t globalIndex, ROOT::NTupleSize_t &nItems)
0423    {
0424       return fPrincipalColumn->MapV<T>(globalIndex, nItems);
0425    }
0426    T *MapV(RNTupleLocalIndex localIndex, ROOT::NTupleSize_t &nItems)
0427    {
0428       return fPrincipalColumn->MapV<T>(localIndex, nItems);
0429    }
0430 
0431    size_t GetValueSize() const final { return sizeof(T); }
0432    size_t GetAlignment() const final { return alignof(T); }
0433 };
0434 
0435 ////////////////////////////////////////////////////////////////////////////////
0436 /// Template specializations for concrete C++ types
0437 ////////////////////////////////////////////////////////////////////////////////
0438 
0439 } // namespace ROOT
0440 
0441 #include "RField/RFieldFundamental.hxx"
0442 #include "RField/RFieldProxiedCollection.hxx"
0443 #include "RField/RFieldRecord.hxx"
0444 #include "RField/RFieldSequenceContainer.hxx"
0445 #include "RField/RFieldSoA.hxx"
0446 #include "RField/RFieldSTLMisc.hxx"
0447 
0448 namespace ROOT {
0449 
0450 template <typename SizeT>
0451 class RField<RNTupleCardinality<SizeT>> final : public RCardinalityField {
0452    using CardinalityType = RNTupleCardinality<SizeT>;
0453 
0454 protected:
0455    std::unique_ptr<ROOT::RFieldBase> CloneImpl(std::string_view newName) const final
0456    {
0457       return std::make_unique<RField>(newName);
0458    }
0459    void ConstructValue(void *where) const final { new (where) CardinalityType(0); }
0460 
0461    /// Get the number of elements of the collection identified by globalIndex
0462    void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
0463    {
0464       RNTupleLocalIndex collectionStart;
0465       ROOT::NTupleSize_t size;
0466       fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &size);
0467       *static_cast<CardinalityType *>(to) = size;
0468    }
0469 
0470    /// Get the number of elements of the collection identified by clusterIndex
0471    void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final
0472    {
0473       RNTupleLocalIndex collectionStart;
0474       ROOT::NTupleSize_t size;
0475       fPrincipalColumn->GetCollectionInfo(localIndex, &collectionStart, &size);
0476       *static_cast<CardinalityType *>(to) = size;
0477    }
0478 
0479    std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final
0480    {
0481       RNTupleLocalIndex collectionStart;
0482       ROOT::NTupleSize_t collectionSize;
0483       fPrincipalColumn->GetCollectionInfo(bulkSpec.fFirstIndex, &collectionStart, &collectionSize);
0484 
0485       auto typedValues = static_cast<CardinalityType *>(bulkSpec.fValues);
0486       typedValues[0] = collectionSize;
0487 
0488       auto lastOffset = collectionStart.GetIndexInCluster() + collectionSize;
0489       ROOT::NTupleSize_t nRemainingEntries = bulkSpec.fCount - 1;
0490       std::size_t nEntries = 1;
0491       while (nRemainingEntries > 0) {
0492          ROOT::NTupleSize_t nItemsUntilPageEnd;
0493          auto offsets =
0494             fPrincipalColumn->MapV<ROOT::Internal::RColumnIndex>(bulkSpec.fFirstIndex + nEntries, nItemsUntilPageEnd);
0495          std::size_t nBatch = std::min(nRemainingEntries, nItemsUntilPageEnd);
0496          for (std::size_t i = 0; i < nBatch; ++i) {
0497             typedValues[nEntries + i] = offsets[i] - lastOffset;
0498             lastOffset = offsets[i];
0499          }
0500          nRemainingEntries -= nBatch;
0501          nEntries += nBatch;
0502       }
0503       return RBulkSpec::kAllSet;
0504    }
0505 
0506 public:
0507    static std::string TypeName() { return "ROOT::RNTupleCardinality<" + RField<SizeT>::TypeName() + ">"; }
0508    explicit RField(std::string_view name) : RCardinalityField(name, TypeName()) {}
0509    RField(RField &&other) = default;
0510    RField &operator=(RField &&other) = default;
0511    ~RField() final = default;
0512 
0513    size_t GetValueSize() const final { return sizeof(CardinalityType); }
0514    size_t GetAlignment() const final { return alignof(CardinalityType); }
0515 };
0516 
0517 /// TObject requires special handling of the fBits and fUniqueID members
0518 template <>
0519 class RField<TObject> final : public RFieldBase {
0520    static std::size_t GetOffsetOfMember(const char *name);
0521    static std::size_t GetOffsetUniqueID() { return GetOffsetOfMember("fUniqueID"); }
0522    static std::size_t GetOffsetBits() { return GetOffsetOfMember("fBits"); }
0523 
0524 private:
0525    RField(std::string_view fieldName, const RField<TObject> &source); ///< Used by CloneImpl()
0526 
0527 protected:
0528    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0529 
0530    void ConstructValue(void *where) const final;
0531    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<TObject>>(); }
0532 
0533    std::size_t AppendImpl(const void *from) final;
0534    void ReadTObject(void *to, UInt_t uniqueID, UInt_t bits);
0535    void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
0536    void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final;
0537 
0538 public:
0539    static std::string TypeName() { return "TObject"; }
0540 
0541    RField(std::string_view fieldName);
0542    RField(RField &&other) = default;
0543    RField &operator=(RField &&other) = default;
0544    ~RField() final = default;
0545 
0546    std::vector<RValue> SplitValue(const RValue &value) const final;
0547    size_t GetValueSize() const final;
0548    size_t GetAlignment() const final;
0549    std::uint32_t GetTypeVersion() const final;
0550    std::uint32_t GetTypeChecksum() const final;
0551    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0552 };
0553 
0554 // Have to be implemented after the definition of all RField<T> types
0555 
0556 namespace Internal {
0557 
0558 /// Helper to check if a given type name is the one expected of Field<T>. Usually, this check can be done by
0559 /// type renormalization of the demangled type name T. The failure case, however, needs to additionally check for
0560 /// ROOT-specific special cases.
0561 template <class T>
0562 bool IsMatchingFieldType(const std::string &actualTypeName)
0563 {
0564    return IsMatchingFieldType(actualTypeName, ROOT::RField<T>::TypeName(), typeid(T));
0565 }
0566 
0567 } // namespace Internal
0568 
0569 template <typename T>
0570 std::unique_ptr<T, typename RFieldBase::RCreateObjectDeleter<T>::deleter> RFieldBase::CreateObject() const
0571 {
0572    if (!Internal::IsMatchingFieldType<T>(GetTypeName())) {
0573       throw RException(
0574          R__FAIL("type mismatch for field " + GetFieldName() + ": " + GetTypeName() + " vs. " + RField<T>::TypeName()));
0575    }
0576    return std::unique_ptr<T>(static_cast<T *>(CreateObjectRawPtr()));
0577 }
0578 
0579 // The void type is specialized in RField.cxx
0580 
0581 template <>
0582 struct RFieldBase::RCreateObjectDeleter<void> {
0583    using deleter = RCreateObjectDeleter<void>;
0584    void operator()(void *);
0585 };
0586 
0587 template <>
0588 std::unique_ptr<void, typename RFieldBase::RCreateObjectDeleter<void>::deleter>
0589 ROOT::RFieldBase::CreateObject<void>() const;
0590 
0591 } // namespace ROOT
0592 
0593 #endif