Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-09 10:18:07

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