diff --git a/core/foundation/inc/TClassEdit.h b/core/foundation/inc/TClassEdit.h index d7baef0221335..9fd8d989add85 100644 --- a/core/foundation/inc/TClassEdit.h +++ b/core/foundation/inc/TClassEdit.h @@ -155,6 +155,8 @@ namespace TClassEdit { void Init(TClassEdit::TInterpreterLookupHelper *helper); std::string CleanType (const char *typeDesc,int mode = 0,const char **tail=0); + inline bool IsArtificial(std::string_view name) { return name.find('@') != name.npos; } + inline bool IsArtificial(ROOT::Internal::TStringView name) {return IsArtificial(std::string_view(name)); } bool IsDefAlloc(const char *alloc, const char *classname); bool IsDefAlloc(const char *alloc, const char *keyclassname, const char *valueclassname); bool IsDefComp (const char *comp , const char *classname); @@ -185,6 +187,16 @@ namespace TClassEdit { inline bool IsUniquePtr(ROOT::Internal::TStringView name) {return IsUniquePtr(std::string_view(name)); } inline bool IsStdArray(std::string_view name) {return 0 == name.compare(0, 6, "array<");} inline bool IsStdArray(ROOT::Internal::TStringView name) {return IsStdArray(std::string_view(name)); } + inline bool IsStdPair(std::string_view name) + { + return 0 == name.compare(0, 10, "std::pair<") || 0 == name.compare(0, 5, "pair<"); + } + inline bool IsStdPair(ROOT::Internal::TStringView name) {return IsStdPair(std::string_view(name)); } + inline bool IsStdPairBase(std::string_view name) + { + return 0 == name.compare(0, 17, "std::__pair_base<") || 0 == name.compare(0, 12, "__pair_base<"); + } + inline bool IsStdPairBase(ROOT::Internal::TStringView name) {return IsStdPair(std::string_view(name)); } inline std::string GetUniquePtrType(std::string_view name) { // Find the first template parameter diff --git a/core/foundation/src/TClassEdit.cxx b/core/foundation/src/TClassEdit.cxx index fc172ea9981bd..4cf84f7e223f2 100644 --- a/core/foundation/src/TClassEdit.cxx +++ b/core/foundation/src/TClassEdit.cxx @@ -838,16 +838,24 @@ void TClassEdit::GetNormalizedName(std::string &norm_name, std::string_view name } norm_name = std::string(name); // NOTE: Is that the shortest version? + + if (TClassEdit::IsArtificial(name)) { + // If there is a @ symbol (followed by a version number) then this is a synthetic class name created + // from an already normalized name for the purpose of supporting schema evolution. + return; + } + // Remove the std:: and default template argument and insert the Long64_t and change basic_string to string. TClassEdit::TSplitType splitname(norm_name.c_str(),(TClassEdit::EModType)(TClassEdit::kLong64 | TClassEdit::kDropStd | TClassEdit::kDropStlDefault | TClassEdit::kKeepOuterConst)); splitname.ShortType(norm_name, TClassEdit::kDropStd | TClassEdit::kDropStlDefault | TClassEdit::kResolveTypedef | TClassEdit::kKeepOuterConst); - if (splitname.fElements.size() == 3 && (splitname.fElements[0] == "std::pair" || splitname.fElements[0] == "pair")) { + // 4 elements expected: "pair", "first type name", "second type name", "trailing stars" + if (splitname.fElements.size() == 4 && (splitname.fElements[0] == "std::pair" || splitname.fElements[0] == "pair" || splitname.fElements[0] == "__pair_base")) { // We don't want to lookup the std::pair itself. std::string first, second; GetNormalizedName(first, splitname.fElements[1]); GetNormalizedName(second, splitname.fElements[2]); - norm_name = "pair<" + first + "," + second; + norm_name = splitname.fElements[0] + "<" + first + "," + second; if (!second.empty() && second.back() == '>') norm_name += " >"; else @@ -1391,7 +1399,7 @@ bool TClassEdit::IsStdClass(const char *classname) classname += StdLen( classname ); if ( strcmp(classname,"string")==0 ) return true; if ( strncmp(classname,"bitset<",strlen("bitset<"))==0) return true; - if ( strncmp(classname,"pair<",strlen("pair<"))==0) return true; + if ( IsStdPair(classname) ) return true; if ( strcmp(classname,"allocator")==0) return true; if ( strncmp(classname,"allocator<",strlen("allocator<"))==0) return true; if ( strncmp(classname,"greater<",strlen("greater<"))==0) return true; diff --git a/core/meta/inc/TVirtualStreamerInfo.h b/core/meta/inc/TVirtualStreamerInfo.h index e99f26adf06b0..d26eef37c4863 100644 --- a/core/meta/inc/TVirtualStreamerInfo.h +++ b/core/meta/inc/TVirtualStreamerInfo.h @@ -127,7 +127,7 @@ class TVirtualStreamerInfo : public TNamed { TVirtualStreamerInfo(TClass * /*cl*/); virtual ~TVirtualStreamerInfo(); virtual void Build() = 0; - virtual void BuildCheck(TFile *file = 0) = 0; + virtual void BuildCheck(TFile *file = 0, Bool_t load = kTRUE) = 0; virtual void BuildEmulated(TFile *file) = 0; virtual void BuildOld() = 0; virtual Bool_t BuildFor( const TClass *cl ) = 0; @@ -180,6 +180,14 @@ class TVirtualStreamerInfo : public TNamed { static void SetCanDelete(Bool_t opt=kTRUE); static void SetFactory(TVirtualStreamerInfo *factory); + // \brief Generate the TClass and TStreamerInfo for the requested pair. + // This creates a TVirtualStreamerInfo for the pair and trigger the BuildCheck/Old to + // provokes the creation of the corresponding TClass. This relies on the dictionary for + // std::pair to already exist (or the interpreter information being available) + // as it is used as a template. + virtual TVirtualStreamerInfo *GenerateInfoForPair(const std::string &pairclassname) = 0; + virtual TVirtualStreamerInfo *GenerateInfoForPair(const std::string &firstname, const std::string &secondname) = 0; + virtual TVirtualCollectionProxy *GenEmulatedProxy(const char* class_name, Bool_t silent) = 0; virtual TClassStreamer *GenEmulatedClassStreamer(const char* class_name, Bool_t silent) = 0; virtual TVirtualCollectionProxy *GenExplicitProxy( const ::ROOT::Detail::TCollectionProxyInfo &info, TClass *cl ) = 0; diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 694177af01503..1a013a8931042 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -54,6 +54,7 @@ In order to access the name of a class within the ROOT type system, the method T #include "TDataMember.h" #include "TDataType.h" #include "TDatime.h" +#include "TEnum.h" #include "TError.h" #include "TExMap.h" #include "TFunctionTemplate.h" @@ -170,6 +171,12 @@ namespace { std::atomic TClass::fgClassCount; +static bool IsFromRootCling() { + // rootcling also uses TCling for generating the dictionary ROOT files. + const static bool foundSymbol = dlsym(RTLD_DEFAULT, "usedToIdentifyRootClingByDlSym"); + return foundSymbol; +} + // Implementation of the TDeclNameRegistry //////////////////////////////////////////////////////////////////////////////// @@ -1108,7 +1115,7 @@ TClass::TClass(const char *name, Bool_t silent) : ::Fatal("TClass::TClass", "gInterpreter not initialized"); gInterpreter->SetClassInfo(this); // sets fClassInfo pointer - if (!silent && !fClassInfo && fName.First('@')==kNPOS) + if (!silent && !fClassInfo && !TClassEdit::IsArtificial(name)) ::Warning("TClass::TClass", "no dictionary for class %s is available", name); ResetBit(kLoading); @@ -1339,7 +1346,11 @@ void TClass::Init(const char *name, Version_t cversion, return; } // Always strip the default STL template arguments (from any template argument or the class name) - fName = TClassEdit::ShortType(name, TClassEdit::kDropStlDefault).c_str(); + if (TClassEdit::IsArtificial(name)) + fName = name; // We can assume that the artificial class name is already normalized. + else + fName = TClassEdit::ShortType(name, TClassEdit::kDropStlDefault).c_str(); + fClassVersion = cversion; fDeclFileName = dfil ? dfil : ""; fImplFileName = ifil ? ifil : ""; @@ -1444,7 +1455,7 @@ void TClass::Init(const char *name, Version_t cversion, // We need to check if the class it is not fwd declared for the cases where we // created a TClass directly in the kForwardDeclared state. Indeed in those cases // fClassInfo will always be nullptr. - if (fState!=kForwardDeclared && !fClassInfo) { + if (fState!=kForwardDeclared && !fClassInfo && !TClassEdit::IsArtificial(fName)) { if (fState == kHasTClassInit) { // If the TClass is being generated from a ROOT dictionary, @@ -1482,7 +1493,7 @@ void TClass::Init(const char *name, Version_t cversion, } } } - if (!silent && (!fClassInfo && !fCanLoadClassInfo) && !isStl && fName.First('@')==kNPOS && + if (!silent && (!fClassInfo && !fCanLoadClassInfo) && !isStl && !TClassEdit::IsArtificial(fName) && !TClassEdit::IsInterpreterDetail(fName.Data()) ) { if (fState == kHasTClassInit) { if (fImplFileLine == -1 && fClassVersion == 0) { @@ -1599,7 +1610,7 @@ void TClass::Init(const char *name, Version_t cversion, fStreamer = TVirtualStreamerInfo::Factory()->GenEmulatedClassStreamer( GetName(), silent ); } } - } else if (!strncmp(GetName(),"std::pair<",10) || !strncmp(GetName(),"pair<",5) ) { + } else if (TClassEdit::IsStdPair(GetName())) { // std::pairs have implicit conversions GetSchemaRules(kTRUE); } @@ -2014,7 +2025,7 @@ void TClass::BuildRealData(void* pointer, Bool_t isTransient) // and those for which the user explicitly requested a dictionary. if (!isTransient && GetState() != kHasTClassInit && TClassEdit::IsStdClass(GetName()) - && strncmp(GetName(), "pair<", 5) != 0) { + && !TClassEdit::IsStdPair(GetName())) { Error("BuildRealData", "Inspection for %s not supported!", GetName()); } @@ -2304,14 +2315,6 @@ Bool_t TClass::CanSplit() const if (!valueClass->CanSplit()) { This->fCanSplit = 0; return kFALSE; } if (valueClass->GetCollectionProxy() != 0) { This->fCanSplit = 0; return kFALSE; } - Int_t stl = -TClassEdit::IsSTLCont(GetName(), 0); - if ((stl==ROOT::kSTLmap || stl==ROOT::kSTLmultimap) - && !valueClass->HasDataMemberInfo()) - { - This->fCanSplit = 0; - return kFALSE; - } - This->fCanSplit = 1; return kTRUE; @@ -2970,6 +2973,15 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent) load = kTRUE; } + if (TClassEdit::IsArtificial(name)) { + // If there is a @ symbol (followed by a version number) then this is a synthetic class name created + // from an already normalized name for the purpose of supporting schema evolution. + // There is no dictionary or interpreter information about this kind of class, the only + // (undesirable) side-effect of doing the search would be a waste of CPU time and potential + // auto-loading or auto-parsing based on the scope of the name. + return cl; + } + // To avoid spurious auto parsing, let's check if the name as-is is // known in the TClassTable. DictFuncPtr_t dict = TClassTable::GetDictNorm(name); @@ -3030,6 +3042,15 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent) // altcl->GetName(), name, normalizedName.c_str()); // } + // We want to avoid auto-parsing due to intentionally missing dictionary for std::pair. + // However, we don't need this special treatement in rootcling (there is no auto-parsing) + // and we want to make that the TClass for the pair goes through the regular creation + // mechanism (i.e. in rootcling they should be in kInterpreted state and never in + // kEmulated state) so that they have proper interpreter (ClassInfo) information which + // will be used to create the TProtoClass (if one is requested for the pair). + const bool ispair = TClassEdit::IsStdPair(normalizedName) && !IsFromRootCling(); + const bool ispairbase = TClassEdit::IsStdPairBase(normalizedName) && !IsFromRootCling(); + TClass *loadedcl = 0; if (checkTable) { loadedcl = LoadClassDefault(normalizedName.c_str(),silent); @@ -3037,8 +3058,11 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent) if (gInterpreter->AutoLoad(normalizedName.c_str(),kTRUE)) { loadedcl = LoadClassDefault(normalizedName.c_str(),silent); } + auto e = TEnum::GetEnum(normalizedName.c_str(), TEnum::kNone); + if (e) + return nullptr; // Maybe this was a typedef: let's try to see if this is the case - if (!loadedcl){ + if (!loadedcl && !ispair && !ispairbase) { if (TDataType* theDataType = gROOT->GetType(normalizedName.c_str())){ // We have a typedef: we get the name of the underlying type auto underlyingTypeName = theDataType->GetTypeName(); @@ -3061,13 +3085,17 @@ TClass *TClass::GetClass(const char *name, Bool_t load, Bool_t silent) // TClass if we have one. if (cl) return cl; - if (TClassEdit::IsSTLCont( normalizedName.c_str() )) { + if (ispair) { + auto pairinfo = TVirtualStreamerInfo::Factory()->GenerateInfoForPair(normalizedName); + return pairinfo ? pairinfo->GetClass() : nullptr; + + } else if (TClassEdit::IsSTLCont( normalizedName.c_str() )) { return gInterpreter->GenerateTClass(normalizedName.c_str(), kTRUE, silent); } // Check the interpreter only after autoparsing the template if any. - { + if (!ispairbase) { std::string::size_type posLess = normalizedName.find('<'); if (posLess != std::string::npos) { gCling->AutoParse(normalizedName.substr(0, posLess).c_str()); @@ -3632,9 +3660,7 @@ TList *TClass::GetListOfEnums(Bool_t requestListLoading /* = kTRUE */) return fEnums.load(); } - static bool fromRootCling = dlsym(RTLD_DEFAULT, "usedToIdentifyRootClingByDlSym"); - - if (fromRootCling) // rootcling is single thread (this save some space in the rootpcm). + if (IsFromRootCling()) // rootcling is single thread (this save some space in the rootpcm). fEnums = new TListOfEnums(this); else fEnums = new TListOfEnumsWithLock(this); @@ -3997,7 +4023,7 @@ void TClass::GetMissingDictionaries(THashTable& result, bool recurse) THashTable visited; - if (strncmp(fName, "pair<", 5) == 0) { + if (TClassEdit::IsStdPair(fName)) { GetMissingDictionariesForPairElements(result, visited, recurse); return; } @@ -6339,7 +6365,7 @@ UInt_t TClass::GetCheckSum(ECheckSum code, Bool_t &isvalid) const // otherwise, on some STL implementations, it can happen that pair has // base classes which are an internal implementation detail. TList *tlb = ((TClass*)this)->GetListOfBases(); - if (tlb && !GetCollectionProxy() && strncmp(GetName(), "pair<", 5)) { + if (tlb && !GetCollectionProxy() && !TClassEdit::IsStdPair(GetName())) { // Loop over bases if not a proxied collection or a pair TIter nextBase(tlb); diff --git a/core/meta/src/TListOfEnums.cxx b/core/meta/src/TListOfEnums.cxx index 582531606dd0a..d06ad74e28e05 100644 --- a/core/meta/src/TListOfEnums.cxx +++ b/core/meta/src/TListOfEnums.cxx @@ -261,7 +261,7 @@ TEnum *TListOfEnums::GetObject(const char *name) const void TListOfEnums::UnmapObject(TObject *obj) { TEnum *e = dynamic_cast(obj); - if (e) { + if (e && e->GetDeclId()) { fIds->Remove((Long64_t)e->GetDeclId()); } } diff --git a/core/meta/src/TSchemaRuleSet.cxx b/core/meta/src/TSchemaRuleSet.cxx index 6b6935634df11..986d0d114288b 100644 --- a/core/meta/src/TSchemaRuleSet.cxx +++ b/core/meta/src/TSchemaRuleSet.cxx @@ -230,8 +230,8 @@ Bool_t TSchemaRuleSet::HasRuleWithSourceClass( const TString &source ) const } } } - } else if (!strncmp(fClass->GetName(),"std::pair<",10) || !strncmp(fClass->GetName(),"pair<",5)) { - if (!strncmp(source,"std::pair<",10) || !strncmp(source,"pair<",5)) { + } else if (TClassEdit::IsStdPair(fClass->GetName())) { + if (TClassEdit::IsStdPair(source)) { // std::pair can be converted into each other if both its parameter can be converted into // each other. TClass *src = TClass::GetClass(source); diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index 24bc553c2722c..6f3febc2889a3 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -4031,7 +4031,31 @@ TCling::CheckClassInfo(const char *name, Bool_t autoload, Bool_t isClassOrNamesp const char *classname = name; - int storeAutoload = SetClassAutoLoading(autoload); + // RAII to suspend and restore auto-loading and auto-parsing based on some external conditions. + class MaybeSuspendAutoLoadParse { + int fStoreAutoLoad = 0; + int fStoreAutoParse = 0; + bool fSuspendedAutoParse = false; + public: + MaybeSuspendAutoLoadParse(int autoload) { + fStoreAutoLoad = ((TCling*)gCling)->SetClassAutoLoading(autoload); + } + + void SuspendAutoParsing() { + fSuspendedAutoParse = true; + fStoreAutoParse = ((TCling*)gCling)->SetSuspendAutoParsing(true); + } + + ~MaybeSuspendAutoLoadParse() { + if (fSuspendedAutoParse) + ((TCling*)gCling)->SetSuspendAutoParsing(fStoreAutoParse); + ((TCling*)gCling)->SetClassAutoLoading(fStoreAutoLoad); + } + }; + + MaybeSuspendAutoLoadParse autoLoadParseRAII( autoload ); + if (TClassEdit::IsStdPair(classname) || TClassEdit::IsStdPairBase(classname)) + autoLoadParseRAII.SuspendAutoParsing(); // First we want to check whether the decl exist, but _without_ // generating any template instantiation. However, the lookup @@ -4077,14 +4101,12 @@ TCling::CheckClassInfo(const char *name, Bool_t autoload, Bool_t isClassOrNamesp // findscope. if (ROOT::TMetaUtils::IsSTLCont(*tmpltDecl)) { // For STL Collection we return kUnknown. - SetClassAutoLoading(storeAutoload); return kUnknown; } } } TClingClassInfo tci(GetInterpreterImpl(), *type); if (!tci.IsValid()) { - SetClassAutoLoading(storeAutoload); return kUnknown; } auto propertiesMask = isClassOrNamespaceOnly ? kIsClass | kIsStruct | kIsNamespace : @@ -4111,19 +4133,16 @@ TCling::CheckClassInfo(const char *name, Bool_t autoload, Bool_t isClassOrNamesp // , hasClassDefInline); // We are now sure that the entry is not in fact an autoload entry. - SetClassAutoLoading(storeAutoload); if (hasClassDefInline) return kWithClassDefInline; else return kKnown; } else { // We are now sure that the entry is not in fact an autoload entry. - SetClassAutoLoading(storeAutoload); return kUnknown; } } - SetClassAutoLoading(storeAutoload); if (decl) return kKnown; else @@ -4135,7 +4154,6 @@ TCling::CheckClassInfo(const char *name, Bool_t autoload, Bool_t isClassOrNamesp TClingTypedefInfo t(fInterpreter, name); if (t.IsValid() && !(t.Property() & kIsFundamental)) { delete[] classname; - SetClassAutoLoading(storeAutoload); return kTRUE; } */ @@ -4146,7 +4164,6 @@ TCling::CheckClassInfo(const char *name, Bool_t autoload, Bool_t isClassOrNamesp // decl = lh.findScope(buf); // } -// SetClassAutoLoading(storeAutoload); // return (decl); } @@ -4364,6 +4381,7 @@ TClass *TCling::GenerateTClass(const char *classname, Bool_t emulation, Bool_t s if (TClassEdit::IsSTLCont(classname)) { version = TClass::GetClass("TVirtualStreamerInfo")->GetClassVersion(); } + R__LOCKGUARD(gInterpreterMutex); TClass *cl = new TClass(classname, version, silent); if (emulation) { cl->SetBit(TClass::kIsEmulation); diff --git a/io/io/inc/TStreamerInfo.h b/io/io/inc/TStreamerInfo.h index 9f8ed1c5954b3..dd537b89bf0d6 100644 --- a/io/io/inc/TStreamerInfo.h +++ b/io/io/inc/TStreamerInfo.h @@ -189,7 +189,7 @@ class TStreamerInfo : public TVirtualStreamerInfo { TStreamerInfo(TClass *cl); virtual ~TStreamerInfo(); void Build(); - void BuildCheck(TFile *file = 0); + void BuildCheck(TFile *file = 0, Bool_t load = kTRUE); void BuildEmulated(TFile *file); void BuildOld(); virtual Bool_t BuildFor( const TClass *cl ); @@ -276,6 +276,14 @@ class TStreamerInfo : public TVirtualStreamerInfo { public: virtual void Update(const TClass *oldClass, TClass *newClass); + // \brief Generate the TClass and TStreamerInfo for the requested pair. + // This creates a TVirtualStreamerInfo for the pair and trigger the BuildCheck/Old to + // provokes the creation of the corresponding TClass. This relies on the dictionary for + // std::pair to already exist (or the interpreter information being available) + // as it is used as a template. + virtual TVirtualStreamerInfo *GenerateInfoForPair(const std::string &pairclassname); + virtual TVirtualStreamerInfo *GenerateInfoForPair(const std::string &firstname, const std::string &secondname); + virtual TVirtualCollectionProxy *GenEmulatedProxy(const char* class_name, Bool_t silent); virtual TClassStreamer *GenEmulatedClassStreamer(const char* class_name, Bool_t silent); virtual TVirtualCollectionProxy *GenExplicitProxy( const ::ROOT::Detail::TCollectionProxyInfo &info, TClass *cl ); diff --git a/io/io/src/TEmulatedCollectionProxy.cxx b/io/io/src/TEmulatedCollectionProxy.cxx index 9a2ac267ef573..deb8e43dc9ebc 100644 --- a/io/io/src/TEmulatedCollectionProxy.cxx +++ b/io/io/src/TEmulatedCollectionProxy.cxx @@ -28,6 +28,7 @@ the class TEmulatedMapProxy. #include "TStreamerInfo.h" #include "TClassEdit.h" #include "TError.h" +#include "TEnum.h" #include "TROOT.h" #include @@ -39,7 +40,6 @@ the class TEmulatedMapProxy. // a dictionary (See end of file for implementation // -static TStreamerElement* R__CreateEmulatedElement(const char *dmName, const char *dmFull, Int_t offset); TStreamerInfo *R__GenerateTClassForPair(const std::string &f, const std::string &s); TEmulatedCollectionProxy::TEmulatedCollectionProxy(const TEmulatedCollectionProxy& copy) @@ -140,18 +140,55 @@ TGenCollectionProxy *TEmulatedCollectionProxy::InitializeEx(Bool_t silent) constexpr size_t kSizeOfPtr = sizeof(void*); return in + (kSizeOfPtr - in%kSizeOfPtr)%kSizeOfPtr; }; + struct GenerateTemporaryTEnum + { + TEnum *fTemporaryTEnum = nullptr; + + GenerateTemporaryTEnum(UInt_t typecase, const std::string &enumname) + { + if (typecase == kIsEnum && !TEnum::GetEnum(enumname.c_str())) { + fTemporaryTEnum = new TEnum(); + fTemporaryTEnum->SetName(enumname.c_str()); + gROOT->GetListOfEnums()->Add(fTemporaryTEnum); + } + } + + ~GenerateTemporaryTEnum() + { + if (fTemporaryTEnum) { + gROOT->GetListOfEnums()->Remove(fTemporaryTEnum); + delete fTemporaryTEnum; + } + } + }; switch ( fSTL_type ) { case ROOT::kSTLmap: case ROOT::kSTLmultimap: nam = "pair<"+inside[1]+","+inside[2]; nam += (nam[nam.length()-1]=='>') ? " >" : ">"; - if (0==TClass::GetClass(nam.c_str())) { - // We need to emulate the pair - R__GenerateTClassForPair(inside[1],inside[2]); - } - fValue = new Value(nam,silent); fKey = new Value(inside[1],silent); fVal = new Value(inside[2],silent); + { + // We have the case of an on-file enum or an unknown class, since + // this comes from a file, we also know that the type was valid but + // since we have no information it was either + // a. an enum + // b. a class of type that was never stored + // c. a class with a custom streamer + // We can "safely" pretend that it is an enum in all 3 case because + // a. obviously + // b. it will not be used anyway (no object of that type on the file) + // c. since we don't know the class we don't have the Streamer and thus can read it anyway + // So let's temporarily pretend it is an enum. + GenerateTemporaryTEnum keyEnum(fKey->fCase, inside[1]); + GenerateTemporaryTEnum valueEnum(fVal->fCase, inside[2]); + + if (0==TClass::GetClass(nam.c_str())) { + // We need to emulate the pair + R__GenerateTClassForPair(inside[1],inside[2]); + } + } + fValue = new Value(nam,silent); if ( !(*fValue).IsValid() || !fKey->IsValid() || !fVal->IsValid() ) { return 0; } @@ -608,105 +645,7 @@ void TEmulatedCollectionProxy::Streamer(TBuffer &b) } } -// -// Utility functions -// -static TStreamerElement* R__CreateEmulatedElement(const char *dmName, const char *dmFull, Int_t offset) -{ - // Create a TStreamerElement for the type 'dmFull' and whose data member name is 'dmName'. - - TString s1( TClassEdit::ShortType(dmFull,0) ); - TString dmType( TClassEdit::ShortType(dmFull,1) ); - Bool_t dmIsPtr = (s1 != dmType); - const char *dmTitle = "Emulation"; - - TDataType *dt = gROOT->GetType(dmType); - if (dt && dt->GetType() > 0 ) { // found a basic type - Int_t dsize,dtype; - dtype = dt->GetType(); - dsize = dt->Size(); - if (dmIsPtr && dtype != kCharStar) { - Error("Pair Emulation Building","%s is not yet supported in pair emulation", - dmFull); - return 0; - } else { - TStreamerElement *el = new TStreamerBasicType(dmName,dmTitle,offset,dtype,dmFull); - el->SetSize(dsize); - return el; - } - } else { - - static const char *full_string_name = "basic_string,allocator >"; - if (strcmp(dmType,"string") == 0 || strcmp(dmType,"std::string") == 0 || strcmp(dmType,full_string_name)==0 ) { - return new TStreamerSTLstring(dmName,dmTitle,offset,dmFull,dmIsPtr); - } - if (TClassEdit::IsSTLCont(dmType)) { - return new TStreamerSTL(dmName,dmTitle,offset,dmFull,dmFull,dmIsPtr); - } - TClass *clm = TClass::GetClass(dmType); - if (!clm) { - // either we have an Emulated enum or a really unknown class! - // let's just claim its an enum :( - Int_t dtype = kInt_t; - return new TStreamerBasicType(dmName,dmTitle,offset,dtype,dmFull); - } - // a pointer to a class - if ( dmIsPtr ) { - if (clm->IsTObject()) { - return new TStreamerObjectPointer(dmName,dmTitle,offset,dmFull); - } else { - return new TStreamerObjectAnyPointer(dmName,dmTitle,offset,dmFull); - } - } - // a class - if (clm->IsTObject()) { - return new TStreamerObject(dmName,dmTitle,offset,dmFull); - } else if(clm == TString::Class() && !dmIsPtr) { - return new TStreamerString(dmName,dmTitle,offset); - } else { - return new TStreamerObjectAny(dmName,dmTitle,offset,dmFull); - } - } -} - - TStreamerInfo *R__GenerateTClassForPair(const std::string &fname, const std::string &sname) { - // Generate a TStreamerInfo for a std::pair - // This TStreamerInfo is then used as if it was read from a file to generate - // and emulated TClass. - - TStreamerInfo *i = (TStreamerInfo*)TClass::GetClass("pair")->GetStreamerInfo()->Clone(); - std::string pname = "pair<"+fname+","+sname; - pname += (pname[pname.length()-1]=='>') ? " >" : ">"; - i->SetName(pname.c_str()); - i->SetClass(0); - i->GetElements()->Delete(); - TStreamerElement *fel = R__CreateEmulatedElement("first", fname.c_str(), 0); - Int_t size = 0; - if (fel) { - i->GetElements()->Add( fel ); - - size = fel->GetSize(); - Int_t sp = sizeof(void *); - //align the non-basic data types (required on alpha and IRIX!!) - if (size%sp != 0) size = size - size%sp + sp; - } else { - delete i; - return 0; - } - TStreamerElement *second = R__CreateEmulatedElement("second", sname.c_str(), size); - if (second) { - i->GetElements()->Add( second ); - } else { - delete i; - return 0; - } - Int_t oldlevel = gErrorIgnoreLevel; - // Hide the warning about the missing pair dictionary. - gErrorIgnoreLevel = kError; - i->BuildCheck(); - gErrorIgnoreLevel = oldlevel; - i->BuildOld(); - return i; + return (TStreamerInfo*)TVirtualStreamerInfo::Factory()->GenerateInfoForPair(fname, sname); } diff --git a/io/io/src/TFile.cxx b/io/io/src/TFile.cxx index 62a87fa995753..b746cd0ae995e 100644 --- a/io/io/src/TFile.cxx +++ b/io/io/src/TFile.cxx @@ -3070,7 +3070,7 @@ void TFile::MakeProject(const char *dirname, const char * /*classes*/, break; } default: - if (strncmp(key->GetName(),"pair<",strlen("pair<"))==0) { + if (TClassEdit::IsStdPair(key->GetName())) { if (genreflex) { tmp.Form("\n",key->GetName()); if ( selections.Index(tmp) == kNPOS ) { diff --git a/io/io/src/TGenCollectionProxy.cxx b/io/io/src/TGenCollectionProxy.cxx index f8efe777d58d6..9aa991990843b 100644 --- a/io/io/src/TGenCollectionProxy.cxx +++ b/io/io/src/TGenCollectionProxy.cxx @@ -15,6 +15,7 @@ #include "TClassEdit.h" #include "TClass.h" #include "TError.h" +#include "TEnum.h" #include "TROOT.h" #include "TInterpreter.h" // For gInterpreterMutex #include "TVirtualMutex.h" @@ -387,10 +388,7 @@ TGenCollectionProxy::Value::Value(const std::string& inside_type, Bool_t silent) // Try to avoid autoparsing. THashTable *typeTable = dynamic_cast( gROOT->GetListOfTypes() ); - THashList *enumTable = dynamic_cast( gROOT->GetListOfEnums() ); - assert(typeTable && "The type of the list of type has changed"); - assert(enumTable && "The type of the list of enum has changed"); TDataType *fundType = (TDataType *)typeTable->THashTable::FindObject( intype.c_str() ); if (fundType && fundType->GetType() < 0x17 && fundType->GetType() > 0) { @@ -404,7 +402,7 @@ TGenCollectionProxy::Value::Value(const std::string& inside_type, Bool_t silent) } else { fSize = fundType->Size(); } - } else if (enumTable->THashList::FindObject( intype.c_str() ) ) { + } else if (TEnum::GetEnum( intype.c_str(), TEnum::kNone) ) { // This is a known enum. fCase = kIsEnum; fSize = sizeof(Int_t); diff --git a/io/io/src/TMakeProject.cxx b/io/io/src/TMakeProject.cxx index 2d8eff1dad381..d3282371389d2 100644 --- a/io/io/src/TMakeProject.cxx +++ b/io/io/src/TMakeProject.cxx @@ -518,7 +518,7 @@ UInt_t TMakeProject::GenerateIncludeForTemplate(FILE *fp, const char *clname, ch AddInclude(fp, what, kTRUE, inclist); fprintf(fp, "namespace std {} using namespace std;\n"); ninc += GenerateIncludeForTemplate(fp, incName, inclist, forward, extrainfos); - } else if (strncmp(incName.Data(), "pair<", strlen("pair<")) == 0) { + } else if (TClassEdit::IsStdPair(incName)) { AddInclude(fp, "utility", kTRUE, inclist); ninc += GenerateIncludeForTemplate(fp, incName, inclist, forward, extrainfos); } else if (strncmp(incName.Data(), "auto_ptr<", strlen("auto_ptr<")) == 0) { @@ -615,7 +615,7 @@ void TMakeProject::GeneratePostDeclaration(FILE *fp, const TVirtualStreamerInfo Int_t stlkind = TClassEdit::STLKind(inside[0]); TClass *key = TClass::GetClass(inside[1].c_str()); TString what; - if (strncmp(inside[1].c_str(),"pair<",strlen("pair<"))==0) { + if (TClassEdit::IsStdPair(inside[1])) { what = inside[1].c_str(); } else if (key) { switch (stlkind) { diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 85a9fdf2bd45b..8866161c381d2 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -52,6 +52,7 @@ element type. #include "TArrayS.h" #include "TArrayL.h" #include "TError.h" +#include "TEnum.h" #include "TRef.h" #include "TProcessID.h" #include "TSystem.h" @@ -311,7 +312,7 @@ void TStreamerInfo::Build() // this is a pair, otherwise, on some STL implementations, it can happen that // pair has mother classes which are an internal implementation detail and // would result in bogus messages printed on screen. - if (strncmp(fClass->GetName(), "pair<", 5)) { + if (!TClassEdit::IsStdPair(fClass->GetName())) { const bool isCollection = fClass->GetCollectionProxy(); const bool isString = !strcmp(fClass->GetName(), "string"); TBaseClass* base = 0; @@ -696,11 +697,11 @@ void TStreamerInfo::Build() /// Check if built and consistent with the class dictionary. /// This method is called by TFile::ReadStreamerInfo. -void TStreamerInfo::BuildCheck(TFile *file /* = 0 */) +void TStreamerInfo::BuildCheck(TFile *file /* = 0 */, Bool_t load /* = kTRUE */) { R__LOCKGUARD(gInterpreterMutex); - fClass = TClass::GetClass(GetName()); + fClass = TClass::GetClass(GetName(), load); if (!fClass) { // fClassVersion should have been a Version_t and/or Version_t // should have been an Int_t. Changing the on-file format @@ -3230,7 +3231,7 @@ UInt_t TStreamerInfo::GetCheckSum(TClass::ECheckSum code) const // Here we skip he base classes in case this is a pair or STL collection, // otherwise, on some STL implementations, it can happen that pair has // base classes which are an internal implementation detail. - if (!fClass->GetCollectionProxy() && strncmp(fClass->GetName(), "pair<", 5)) { + if (!fClass->GetCollectionProxy() && !TClassEdit::IsStdPair(fClass->GetName())) { while ( (el=(TStreamerElement*)next())) { // loop over bases if (el->IsBase()) { name = el->GetName(); @@ -3855,7 +3856,7 @@ UInt_t TStreamerInfo::GenerateIncludes(FILE *fp, char *inclist, const TList *ext if (strncmp(include,"include\\",9)==0) { include += 9; } - if (strncmp(element->GetTypeName(),"pair<",strlen("pair<"))==0) { + if (TClassEdit::IsStdPair(element->GetTypeName())) { TMakeProject::AddInclude( fp, "utility", kTRUE, inclist); } else if (strncmp(element->GetTypeName(),"auto_ptr<",strlen("auto_ptr<"))==0) { TMakeProject::AddInclude( fp, "memory", kTRUE, inclist); @@ -3881,7 +3882,7 @@ Int_t TStreamerInfo::GenerateHeaderFile(const char *dirname, const TList *subCla { // if (fClassVersion == -4) return 0; if ((fClass && fClass->GetCollectionType()) || TClassEdit::IsSTLCont(GetName())) return 0; - if (strncmp(GetName(),"pair<",strlen("pair<"))==0) return 0; + if (TClassEdit::IsStdPair(GetName())) return 0; if (strncmp(GetName(),"auto_ptr<",strlen("auto_ptr<"))==0) return 0; TClass *cl = TClass::GetClass(GetName()); @@ -5547,3 +5548,134 @@ TStreamerInfo::GenExplicitClassStreamer( const ::ROOT::TCollectionProxyInfo &inf { return TCollectionProxyFactory::GenExplicitClassStreamer(info, cl); } + +// +// Utility functions +// +static TStreamerElement* R__CreateEmulatedElement(const char *dmName, const std::string &dmFull, Int_t offset) +{ + // Create a TStreamerElement for the type 'dmFull' and whose data member name is 'dmName'. + + TString s1( TClassEdit::ShortType(dmFull.c_str(),0) ); + TString dmType( TClassEdit::ShortType(dmFull.c_str(),1) ); + Bool_t dmIsPtr = (s1 != dmType); + const char *dmTitle = "Emulation"; + + TDataType *dt = gROOT->GetType(dmType); + if (dt && dt->GetType() > 0 ) { // found a basic type + Int_t dsize,dtype; + dtype = dt->GetType(); + dsize = dt->Size(); + if (dmIsPtr && dtype != kCharStar) { + Error("Pair Emulation Building","%s is not yet supported in pair emulation", + dmFull.c_str()); + return 0; + } else { + TStreamerElement *el = new TStreamerBasicType(dmName,dmTitle,offset,dtype,dmFull.c_str()); + el->SetSize(dsize); + return el; + } + } else { + + static const char *full_string_name = "basic_string,allocator >"; + if (strcmp(dmType,"string") == 0 || strcmp(dmType,"std::string") == 0 || strcmp(dmType,full_string_name)==0 ) { + return new TStreamerSTLstring(dmName,dmTitle,offset,dmFull.c_str(),dmIsPtr); + } + if (TClassEdit::IsSTLCont(dmType)) { + return new TStreamerSTL(dmName,dmTitle,offset,dmFull.c_str(),dmFull.c_str(),dmIsPtr); + } + TClass *clm = TClass::GetClass(dmType); + if (!clm) { + if (TEnum::GetEnum(dmType, TEnum::kNone)) { + Int_t dtype = kInt_t; + return new TStreamerBasicType(dmName,dmTitle,offset,dtype,dmFull.c_str()); + } + return nullptr; + // either we have an Emulated enum or a really unknown class! + // let's just claim its an enum :( + //Int_t dtype = kInt_t; + //return new TStreamerBasicType(dmName,dmTitle,offset,dtype,dmFull.c_str()); + } + // a pointer to a class + if ( dmIsPtr ) { + if (clm->IsTObject()) { + return new TStreamerObjectPointer(dmName,dmTitle,offset,dmFull.c_str()); + } else { + return new TStreamerObjectAnyPointer(dmName,dmTitle,offset,dmFull.c_str()); + } + } + // a class + if (clm->IsTObject()) { + return new TStreamerObject(dmName,dmTitle,offset,dmFull.c_str()); + } else if(clm == TString::Class() && !dmIsPtr) { + return new TStreamerString(dmName,dmTitle,offset); + } else { + return new TStreamerObjectAny(dmName,dmTitle,offset,dmFull.c_str()); + } + } +} + +// \brief Generate the TClass and TStreamerInfo for the requested pair. +// This creates a TVirtualStreamerInfo for the pair and trigger the BuildCheck/Old to +// provoke the creation of the corresponding TClass. This relies on the dictionary for +// std::pair to already exist (or the interpreter information being available) +// as it is used as a template. +TVirtualStreamerInfo *TStreamerInfo::GenerateInfoForPair(const std::string &firstname, const std::string &secondname) +{ + // Generate a TStreamerInfo for a std::pair + // This TStreamerInfo is then used as if it was read from a file to generate + // and emulated TClass. + + TStreamerInfo *i = (TStreamerInfo*)TClass::GetClass("pair")->GetStreamerInfo()->Clone(); + std::string pname = "pair<" + firstname + "," + secondname; + pname += (pname[pname.length()-1]=='>') ? " >" : ">"; + i->SetName(pname.c_str()); + i->SetClass(nullptr); + i->GetElements()->Delete(); + TStreamerElement *fel = R__CreateEmulatedElement("first", firstname, 0); + Int_t size = 0; + if (fel) { + i->GetElements()->Add( fel ); + + size = fel->GetSize(); + Int_t sp = sizeof(void *); + //align the non-basic data types (required on alpha and IRIX!!) + if (size%sp != 0) size = size - size%sp + sp; + } else { + delete i; + return 0; + } + TStreamerElement *second = R__CreateEmulatedElement("second", secondname, size); + if (second) { + i->GetElements()->Add( second ); + } else { + delete i; + return 0; + } + Int_t oldlevel = gErrorIgnoreLevel; + // Hide the warning about the missing pair dictionary. + gErrorIgnoreLevel = kError; + i->BuildCheck(nullptr, kFALSE); // Skipping the loading part (it would leads to infinite recursion on this very routine) + gErrorIgnoreLevel = oldlevel; + i->BuildOld(); + return i; +} + +TVirtualStreamerInfo *TStreamerInfo::GenerateInfoForPair(const std::string &pairclassname) +{ + const static int pairlen = strlen("pair<"); + if (pairclassname.compare(0, pairlen, "pair<") != 0) { + Error("GenerateInfoForPair", "The class name passed is not a pair: %s", pairclassname.c_str()); + return nullptr; + } + + std::vector inside; + int nested = 0; + int num = TClassEdit::GetSplit(pairclassname.c_str(), inside, nested); + if (num != 4) { + Error("GenerateInfoForPair", "Could not find the pair arguments in %s", pairclassname.c_str()); + return nullptr; + } + + return GenerateInfoForPair(inside[1], inside[2]); +} diff --git a/tree/treeplayer/src/TTreeGeneratorBase.cxx b/tree/treeplayer/src/TTreeGeneratorBase.cxx index edbd89c3c306f..7a703ef47a7af 100644 --- a/tree/treeplayer/src/TTreeGeneratorBase.cxx +++ b/tree/treeplayer/src/TTreeGeneratorBase.cxx @@ -116,10 +116,12 @@ namespace Internal { } } directive = Form("#include \"%s\"\n",filename); - } else if (!strncmp(cl->GetName(), "pair<", 5) - || !strncmp(cl->GetName(), "std::pair<", 10)) { + } else if (TClassEdit::IsStdPair(cl->GetName())) { TClassEdit::TSplitType split(cl->GetName()); - if (split.fElements.size() == 3) { + // 4 elements expected: "pair", "first type name", "second type name", "trailing stars" + // However legacy code had a test for 3, we will leave it here until + // a test is developed (or found :) ) that exercise these lines of code. + if (split.fElements.size() == 3 || split.fElements.size() == 4) { for (int arg = 1; arg < 3; ++arg) { TClass* clArg = TClass::GetClass(split.fElements[arg].c_str()); if (clArg) AddHeader(clArg);