Back to home page

EIC code displayed by LXR

 
 

    


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

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    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0201 };
0202 
0203 /// The field for a class using ROOT standard streaming
0204 class RStreamerField final : public RFieldBase {
0205 private:
0206    class RStreamerFieldDeleter : public RDeleter {
0207    private:
0208       TClass *fClass;
0209 
0210    public:
0211       explicit RStreamerFieldDeleter(TClass *cl) : fClass(cl) {}
0212       void operator()(void *objPtr, bool dtorOnly) final;
0213    };
0214 
0215    TClass *fClass = nullptr;
0216    ROOT::Internal::RNTupleSerializer::StreamerInfoMap_t fStreamerInfos; ///< streamer info records seen during writing
0217    ROOT::Internal::RColumnIndex fIndex;                           ///< number of bytes written in the current cluster
0218 
0219 private:
0220    RStreamerField(std::string_view fieldName, TClass *classp);
0221 
0222 protected:
0223    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0224 
0225    const RColumnRepresentations &GetColumnRepresentations() const final;
0226    void GenerateColumns() final;
0227    void GenerateColumns(const ROOT::RNTupleDescriptor &) final;
0228 
0229    void ConstructValue(void *where) const final;
0230    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RStreamerFieldDeleter>(fClass); }
0231 
0232    std::size_t AppendImpl(const void *from) final;
0233    void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
0234 
0235    void CommitClusterImpl() final { fIndex = 0; }
0236 
0237    bool HasExtraTypeInfo() const final { return true; }
0238    // Returns the list of seen streamer infos
0239    ROOT::RExtraTypeInfoDescriptor GetExtraTypeInfo() const final;
0240 
0241    void BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final;
0242 
0243 public:
0244    RStreamerField(std::string_view fieldName, std::string_view className, std::string_view typeAlias = "");
0245    RStreamerField(RStreamerField &&other) = default;
0246    RStreamerField &operator=(RStreamerField &&other) = default;
0247    ~RStreamerField() final = default;
0248 
0249    size_t GetValueSize() const final;
0250    size_t GetAlignment() const final;
0251    std::uint32_t GetTypeVersion() const final;
0252    std::uint32_t GetTypeChecksum() const final;
0253    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0254 };
0255 
0256 /// The field for an unscoped or scoped enum with dictionary
0257 class REnumField : public RFieldBase {
0258 private:
0259    REnumField(std::string_view fieldName, TEnum *enump);
0260    REnumField(std::string_view fieldName, std::string_view enumName, std::unique_ptr<RFieldBase> intField);
0261 
0262 protected:
0263    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0264 
0265    void ConstructValue(void *where) const final { CallConstructValueOn(*fSubfields[0], where); }
0266 
0267    std::size_t AppendImpl(const void *from) final { return CallAppendOn(*fSubfields[0], from); }
0268    void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final { CallReadOn(*fSubfields[0], globalIndex, to); }
0269    void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final { CallReadOn(*fSubfields[0], localIndex, to); }
0270 
0271 public:
0272    REnumField(std::string_view fieldName, std::string_view enumName);
0273    REnumField(REnumField &&other) = default;
0274    REnumField &operator=(REnumField &&other) = default;
0275    ~REnumField() override = default;
0276 
0277    std::vector<RValue> SplitValue(const RValue &value) const final;
0278    size_t GetValueSize() const final { return fSubfields[0]->GetValueSize(); }
0279    size_t GetAlignment() const final { return fSubfields[0]->GetAlignment(); }
0280    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0281 };
0282 
0283 /// Classes with dictionaries that can be inspected by TClass
0284 template <typename T, typename = void>
0285 class RField final : public RClassField {
0286 public:
0287    static std::string TypeName() { return ROOT::Internal::GetRenormalizedDemangledTypeName(typeid(T)); }
0288    RField(std::string_view name) : RClassField(name, TypeName())
0289    {
0290       static_assert(std::is_class_v<T>, "no I/O support for this basic C++ type");
0291    }
0292    RField(RField &&other) = default;
0293    RField &operator=(RField &&other) = default;
0294    ~RField() final = default;
0295 };
0296 
0297 template <typename T>
0298 class RField<T, typename std::enable_if<std::is_enum_v<T>>::type> final : public REnumField {
0299 public:
0300    static std::string TypeName() { return ROOT::Internal::GetDemangledTypeName(typeid(T)); }
0301    RField(std::string_view name) : REnumField(name, TypeName()) {}
0302    RField(RField &&other) = default;
0303    RField &operator=(RField &&other) = default;
0304    ~RField() final = default;
0305 };
0306 
0307 /// An artificial field that transforms an RNTuple column that contains the offset of collections into
0308 /// collection sizes. It is only used for reading, e.g. as projected field or as an artificial field that provides the
0309 /// "number of" RDF columns for collections (e.g. `R_rdf_sizeof_jets` for a collection named `jets`).
0310 /// It is used in the templated RField<RNTupleCardinality<SizeT>> form, which represents the collection sizes either
0311 /// as 32bit unsigned int (std::uint32_t) or as 64bit unsigned int (std::uint64_t).
0312 class RCardinalityField : public RFieldBase {
0313    friend class ROOT::RNTupleCollectionView; // to access GetCollectionInfo()
0314 
0315 private:
0316    void GetCollectionInfo(ROOT::NTupleSize_t globalIndex, RNTupleLocalIndex *collectionStart, ROOT::NTupleSize_t *size)
0317    {
0318       fPrincipalColumn->GetCollectionInfo(globalIndex, collectionStart, size);
0319    }
0320    void GetCollectionInfo(RNTupleLocalIndex localIndex, RNTupleLocalIndex *collectionStart, ROOT::NTupleSize_t *size)
0321    {
0322       fPrincipalColumn->GetCollectionInfo(localIndex, collectionStart, size);
0323    }
0324 
0325 protected:
0326    RCardinalityField(std::string_view fieldName, std::string_view typeName)
0327       : RFieldBase(fieldName, typeName, ROOT::ENTupleStructure::kLeaf, false /* isSimple */)
0328    {
0329    }
0330 
0331    const RColumnRepresentations &GetColumnRepresentations() const final;
0332    // Field is only used for reading
0333    void GenerateColumns() final { throw RException(R__FAIL("Cardinality fields must only be used for reading")); }
0334    void GenerateColumns(const ROOT::RNTupleDescriptor &) final;
0335 
0336 public:
0337    RCardinalityField(RCardinalityField &&other) = default;
0338    RCardinalityField &operator=(RCardinalityField &&other) = default;
0339    ~RCardinalityField() override = default;
0340 
0341    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0342 
0343    const RField<RNTupleCardinality<std::uint32_t>> *As32Bit() const;
0344    const RField<RNTupleCardinality<std::uint64_t>> *As64Bit() const;
0345 };
0346 
0347 template <typename T>
0348 class RSimpleField : public RFieldBase {
0349 protected:
0350    void GenerateColumns() override { GenerateColumnsImpl<T>(); }
0351    void GenerateColumns(const ROOT::RNTupleDescriptor &desc) override { GenerateColumnsImpl<T>(desc); }
0352 
0353    void ConstructValue(void *where) const final { new (where) T{0}; }
0354 
0355 public:
0356    RSimpleField(std::string_view name, std::string_view type)
0357       : RFieldBase(name, type, ROOT::ENTupleStructure::kLeaf, true /* isSimple */)
0358    {
0359       fTraits |= kTraitTrivialType;
0360    }
0361    RSimpleField(RSimpleField &&other) = default;
0362    RSimpleField &operator=(RSimpleField &&other) = default;
0363    ~RSimpleField() override = default;
0364 
0365    T *Map(ROOT::NTupleSize_t globalIndex) { return fPrincipalColumn->Map<T>(globalIndex); }
0366    T *Map(RNTupleLocalIndex localIndex) { return fPrincipalColumn->Map<T>(localIndex); }
0367    T *MapV(ROOT::NTupleSize_t globalIndex, ROOT::NTupleSize_t &nItems)
0368    {
0369       return fPrincipalColumn->MapV<T>(globalIndex, nItems);
0370    }
0371    T *MapV(RNTupleLocalIndex localIndex, ROOT::NTupleSize_t &nItems)
0372    {
0373       return fPrincipalColumn->MapV<T>(localIndex, nItems);
0374    }
0375 
0376    size_t GetValueSize() const final { return sizeof(T); }
0377    size_t GetAlignment() const final { return alignof(T); }
0378 };
0379 
0380 ////////////////////////////////////////////////////////////////////////////////
0381 /// Template specializations for concrete C++ types
0382 ////////////////////////////////////////////////////////////////////////////////
0383 
0384 } // namespace ROOT
0385 
0386 #include "RField/RFieldFundamental.hxx"
0387 #include "RField/RFieldProxiedCollection.hxx"
0388 #include "RField/RFieldRecord.hxx"
0389 #include "RField/RFieldSequenceContainer.hxx"
0390 #include "RField/RFieldSTLMisc.hxx"
0391 
0392 namespace ROOT {
0393 
0394 template <typename SizeT>
0395 class RField<RNTupleCardinality<SizeT>> final : public RCardinalityField {
0396 protected:
0397    std::unique_ptr<ROOT::RFieldBase> CloneImpl(std::string_view newName) const final
0398    {
0399       return std::make_unique<RField<RNTupleCardinality<SizeT>>>(newName);
0400    }
0401    void ConstructValue(void *where) const final { new (where) RNTupleCardinality<SizeT>(0); }
0402 
0403    /// Get the number of elements of the collection identified by globalIndex
0404    void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
0405    {
0406       RNTupleLocalIndex collectionStart;
0407       ROOT::NTupleSize_t size;
0408       fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &size);
0409       *static_cast<RNTupleCardinality<SizeT> *>(to) = size;
0410    }
0411 
0412    /// Get the number of elements of the collection identified by clusterIndex
0413    void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final
0414    {
0415       RNTupleLocalIndex collectionStart;
0416       ROOT::NTupleSize_t size;
0417       fPrincipalColumn->GetCollectionInfo(localIndex, &collectionStart, &size);
0418       *static_cast<RNTupleCardinality<SizeT> *>(to) = size;
0419    }
0420 
0421    std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final
0422    {
0423       RNTupleLocalIndex collectionStart;
0424       ROOT::NTupleSize_t collectionSize;
0425       fPrincipalColumn->GetCollectionInfo(bulkSpec.fFirstIndex, &collectionStart, &collectionSize);
0426 
0427       auto typedValues = static_cast<RNTupleCardinality<SizeT> *>(bulkSpec.fValues);
0428       typedValues[0] = collectionSize;
0429 
0430       auto lastOffset = collectionStart.GetIndexInCluster() + collectionSize;
0431       ROOT::NTupleSize_t nRemainingEntries = bulkSpec.fCount - 1;
0432       std::size_t nEntries = 1;
0433       while (nRemainingEntries > 0) {
0434          ROOT::NTupleSize_t nItemsUntilPageEnd;
0435          auto offsets =
0436             fPrincipalColumn->MapV<ROOT::Internal::RColumnIndex>(bulkSpec.fFirstIndex + nEntries, nItemsUntilPageEnd);
0437          std::size_t nBatch = std::min(nRemainingEntries, nItemsUntilPageEnd);
0438          for (std::size_t i = 0; i < nBatch; ++i) {
0439             typedValues[nEntries + i] = offsets[i] - lastOffset;
0440             lastOffset = offsets[i];
0441          }
0442          nRemainingEntries -= nBatch;
0443          nEntries += nBatch;
0444       }
0445       return RBulkSpec::kAllSet;
0446    }
0447 
0448 public:
0449    static std::string TypeName() { return "ROOT::RNTupleCardinality<" + RField<SizeT>::TypeName() + ">"; }
0450    explicit RField(std::string_view name) : RCardinalityField(name, TypeName()) {}
0451    RField(RField &&other) = default;
0452    RField &operator=(RField &&other) = default;
0453    ~RField() final = default;
0454 
0455    size_t GetValueSize() const final { return sizeof(RNTupleCardinality<SizeT>); }
0456    size_t GetAlignment() const final { return alignof(RNTupleCardinality<SizeT>); }
0457 };
0458 
0459 /// TObject requires special handling of the fBits and fUniqueID members
0460 template <>
0461 class RField<TObject> final : public RFieldBase {
0462    static std::size_t GetOffsetOfMember(const char *name);
0463    static std::size_t GetOffsetUniqueID() { return GetOffsetOfMember("fUniqueID"); }
0464    static std::size_t GetOffsetBits() { return GetOffsetOfMember("fBits"); }
0465 
0466 private:
0467    RField(std::string_view fieldName, const RField<TObject> &source); ///< Used by CloneImpl()
0468 
0469 protected:
0470    std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
0471 
0472    void ConstructValue(void *where) const final;
0473    std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<TObject>>(); }
0474 
0475    std::size_t AppendImpl(const void *from) final;
0476    void ReadTObject(void *to, UInt_t uniqueID, UInt_t bits);
0477    void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
0478    void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final;
0479 
0480    void AfterConnectPageSource() final;
0481 
0482 public:
0483    static std::string TypeName() { return "TObject"; }
0484 
0485    RField(std::string_view fieldName);
0486    RField(RField &&other) = default;
0487    RField &operator=(RField &&other) = default;
0488    ~RField() final = default;
0489 
0490    std::vector<RValue> SplitValue(const RValue &value) const final;
0491    size_t GetValueSize() const final;
0492    size_t GetAlignment() const final;
0493    std::uint32_t GetTypeVersion() const final;
0494    std::uint32_t GetTypeChecksum() const final;
0495    void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
0496 };
0497 
0498 // Has to be implemented after the definition of all RField<T> types
0499 // The void type is specialized in RField.cxx
0500 
0501 template <typename T>
0502 std::unique_ptr<T, typename RFieldBase::RCreateObjectDeleter<T>::deleter> RFieldBase::CreateObject() const
0503 {
0504    if (GetTypeName() != RField<T>::TypeName()) {
0505       throw RException(
0506          R__FAIL("type mismatch for field " + GetFieldName() + ": " + GetTypeName() + " vs. " + RField<T>::TypeName()));
0507    }
0508    return std::unique_ptr<T>(static_cast<T *>(CreateObjectRawPtr()));
0509 }
0510 
0511 template <>
0512 struct RFieldBase::RCreateObjectDeleter<void> {
0513    using deleter = RCreateObjectDeleter<void>;
0514    void operator()(void *);
0515 };
0516 
0517 template <>
0518 std::unique_ptr<void, typename RFieldBase::RCreateObjectDeleter<void>::deleter>
0519 ROOT::RFieldBase::CreateObject<void>() const;
0520 
0521 } // namespace ROOT
0522 
0523 #endif