Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 10:29:57

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