ATLAS Offline Software
Loading...
Searching...
No Matches
TBranchAuxDynReader.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
11#include "RootUtils/Type.h"
12
13#include "TBranchAuxDynReader.h"
14#include "TBranchAuxDynStore.h"
15#include "RootAuxDynIO.h"
16
17#include "TTree.h"
18#include "TBranch.h"
19#include "TClass.h"
20#include "TClassTable.h"
21#include "TClassEdit.h"
22#include "TVirtualCollectionProxy.h"
23#include "TROOT.h"
24#include "TDictAttributeMap.h"
25
26using std::string;
27
28namespace {
29
30const std::type_info* dataTypeToTypeInfo (EDataType type, std::string& typeName)
31{
33 typeName = typ.getTypeName();
34 return typ.getTypeInfo();
35}
36
37
53const std::type_info*
54getAuxElementType( TClass *expectedClass, EDataType expectedType, bool standalone,
55 std::string& elementTypeName, std::string& storageTypeName)
56{
57 if (standalone) {
58 if(expectedClass) {
59 elementTypeName = expectedClass->GetName();
60 storageTypeName = elementTypeName;
61 return expectedClass->GetTypeInfo();
62 }
63 const std::type_info* ret = dataTypeToTypeInfo(expectedType, elementTypeName);
64 storageTypeName = elementTypeName;
65 return ret;
66 }
67
68 // Not standalone; branch should be a vector.
69 if (!expectedClass) return 0;
70
71 storageTypeName = expectedClass->GetName();
72 if (strncmp (expectedClass->GetName(), "vector<", 7) == 0) {
73 TVirtualCollectionProxy* prox = expectedClass->GetCollectionProxy();
74 if (!prox) return 0;
75 if (prox->GetValueClass()) {
76 elementTypeName = prox->GetValueClass()->GetName();
77 return prox->GetValueClass()->GetTypeInfo();
78 }
79 return dataTypeToTypeInfo (prox->GetType(), elementTypeName);
80 }
81 else if (strncmp (expectedClass->GetName(), "SG::PackedContainer<", 20) == 0){
82 elementTypeName.clear();
83 {
84 // Protect against data race inside TClassEdit.
85 // https://github.com/root-project/root/issues/10353
86 // Should be fixed in root 6.26.02.
87 R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
88 TClassEdit::TSplitType split (expectedClass->GetName());
89 if (split.fElements.size() > 1) {
90 elementTypeName = split.fElements[1];
91 }
92 }
93 if (!elementTypeName.empty()) {
94 RootUtils::Type typ (elementTypeName);
95 return typ.getTypeInfo();
96 }
97 }
98 return 0;
99}
100
101
103getAuxIdForAttribute(const std::string& attr, TClass *tclass, EDataType edt, bool standalone,
104 SG::auxid_t linked_auxid)
105{
107
108 SG::auxid_t auxid = r.findAuxID(attr);
109 if(auxid != SG::null_auxid)
110 return auxid;
111
112 string elemen_type_name;
113 string branch_type_name;
114 const std::type_info* ti = getAuxElementType(tclass, edt, standalone, elemen_type_name, branch_type_name);
115 if( !ti )
116 return auxid;
117
118 return SG::getDynamicAuxID (*ti, attr, elemen_type_name, branch_type_name, standalone,
119 linked_auxid);
120}
121
122
123std::string
124getKeyFromBranch(TBranch* branch)
125{
126 TClass *tc = nullptr;
127 EDataType type;
128 if( branch->GetExpectedType(tc, type) == 0 && tc ) {
129 std::string key = branch->GetName();
130 const std::string clname_pfx = std::string(tc->GetName()) + '_';
131 if( key.starts_with( clname_pfx ) ) {
132 key.erase(0, clname_pfx.size());
133 }
135 return key;
136 }
137 return "";
138}
139
140} // anonymous namespace
141
142
144{
145 if( needsSE ) {
146 if( (edtyp == kULong_t or edtyp == kULong64_t or edtyp == kLong_t or edtyp == kLong64_t) and
147 (SE_edt == kULong_t or SE_edt == kULong64_t or SE_edt == kLong_t or SE_edt == kLong64_t) and
148 sizeof(Long_t) == sizeof(Long64_t) ) {
149 // There is no need to attempt ROOT schema evolution between these types (and it will not work anyhow)
150 needsSE = false;
151 }
152 }
153 if( needsSE ) {
154 // reading through the TTree - allows for schema evolution
155 int rc = branch->GetTree()->SetBranchAddress( branch->GetName(), data, SE_tclass, SE_edt, true);
156 if( rc < 0 ) {
157 std::ostringstream msg;
158 msg << "SetBranchAddress() failed for " << branch->GetName() << " error=" << rc;
159 throw msg.str();
160 }
161 } else {
162 branch->SetAddress(data);
163 }
164}
165
166
167// ----------------- TBranchAuxDynReader ---------------------------------------
168
169// Fix Reader for a specific tree and branch base name.
170// Find all dynamic attribute branches that share the base name
172 : m_baseBranchName( base_branch->GetName() ),
173 m_key( getKeyFromBranch(base_branch) ),
174 m_tree( tree )
175{
176 // The Branch here is the object (AuxContainer) branch, not the attribute branch
177 TClass *tc = nullptr, *storeTC = nullptr;
178 EDataType type;
179 (void)base_branch->GetExpectedType(tc, type); //MN: Errors would be caught in isAuxDynBranch() earlier
180 if( tc ) storeTC = tc->GetBaseClass("SG::IAuxStoreHolder");
181 if( storeTC ) m_storeHolderOffset = tc->GetBaseClassOffset( storeTC );
182 if( m_storeHolderOffset < 0 ) {
183 const std::string name = (tc? tc->GetName() : m_baseBranchName);
184 errorcheck::ReportMessage msg (MSG::INFO, ERRORCHECK_ARGS, "TBranchAuxDynReader");
185 msg << "IAuxStoreHolder interface not found in " << name << " - will not read dynamic attributes";
186 }
187
189 string branch_prefix = RootAuxDynIO::auxBranchName("", m_baseBranchName);
190 TObjArray *all_branches = m_tree->GetListOfBranches();
191 for( int i=0; i<all_branches->GetEntriesFast(); i++ ) {
192 const char *bname = (*all_branches)[i]->GetName();
193 if( strncmp(bname, branch_prefix.c_str(), branch_prefix.size()) == 0 ) {
194 const string attr_inFile = bname+branch_prefix.size();
195 const string attr = r.inputRename (m_key, attr_inFile);
196 m_branchMap[attr] = (TBranch*)(*all_branches)[i];
197 }
198 }
199}
200
201
203 const std::string& attr,
204 TBranch* branch)
205{
206 TClass* expectedClass = 0;
207 EDataType expectedType = kOther_t;
208 if( branch->GetExpectedType(expectedClass, expectedType) != 0) {
209 // raise hell
210 }
211
212 SG::auxid_t linked_auxid = SG::null_auxid;
213 if (expectedClass) {
214 std::string className = expectedClass->GetName();
216 std::string linkedAttr = SG::AuxTypeRegistry::linkedName (attr);
217 auto it = m_branchMap.find (linkedAttr);
218 if (it != m_branchMap.end()) {
219 linked_auxid = initBranch (standalone, linkedAttr, it->second);
220 }
221 if (linked_auxid == SG::null_auxid) {
222 errorcheck::ReportMessage msg (MSG::WARNING, ERRORCHECK_ARGS, "TBranchAuxDynReader::initBranch");
223 msg << "Could not find linked variable for " << branch->GetName()
224 << " type: " << expectedClass->GetName();
225 }
226 }
227 }
228
229 SG::auxid_t auxid = getAuxIdForAttribute(attr, expectedClass, expectedType, standalone,
230 linked_auxid);
231
232 // add AuxID to the list
233 // May still be null if we don't have a dictionary for the branch.
234 if (auxid != SG::null_auxid) {
235 addAuxID(auxid);
236 } else {
237 errorcheck::ReportMessage msg (MSG::WARNING, ERRORCHECK_ARGS, "TBranchAuxDynReader::initBranch");
238 msg << "Could not find auxid for " << branch->GetName()
239 << " type: " << expectedClass->GetName();
240
241 }
242 return auxid;
243}
244
245// Has to be a separate method because 'standalone' status is not know at construction time
246// Prepare all branch infos for dynamic attributes (auxids and types)
247void TBranchAuxDynReader::init(bool standalone)
248{
249 if( m_initialized ) return;
250
251 for( const auto& attr2branch: m_branchMap ) {
252 initBranch (standalone, attr2branch.first, attr2branch.second);
253 }
254 m_initialized = true;
255}
256
257// Called by the AuxStore when it is reading new attribute data from the file
258// All information is cached in a BranchInfo object for better performance
261{
262 BranchInfo& brInfo = m_branchInfos[auxid];
263 if( brInfo.status == BranchInfo::NotInitialized )
264 {
266 brInfo.auxid = auxid;
267 brInfo.attribName = r.getName(auxid);
268 // Don't match this attribute if it's been renamed.
269 // For example, suppose we've renamed attribute `foo' to `foo_old',
270 // and someone then comes and asks for `foo'.
271 // `foo' will not be found in the m_branchMap test below
272 // (`foo_old' will be in this map). However, in the following
273 // else clause, we'll recreate the branch name from `foo'.
274 // This branch exists (renaming is only in the transient store),
275 // so if we didn't have the condition here, then we'd then
276 // make a `foo' attribute from that branch.
277 if (r.inputRename (m_key, brInfo.attribName) != brInfo.attribName) {
279 return brInfo;
280 }
281
282 auto it = m_branchMap.find (brInfo.attribName);
283 if (it != m_branchMap.end()) {
284 brInfo.branch = it->second;
285 }
286 else {
287 const string aux_branch_name = RootAuxDynIO::auxBranchName(brInfo.attribName, m_baseBranchName);
288 brInfo.branch = m_tree->GetBranch( aux_branch_name.c_str() );
289 }
290
291 // mark initialized here so it remembers this branch was not found
292 if( !brInfo.branch ) {
294 return brInfo;
295 }
296 if( brInfo.branch->GetExpectedType( brInfo.tclass, brInfo.edtyp) ) {
298 throw string("Error getting branch type for ") + brInfo.branch->GetName();
299 }
300
301 if( !store.standalone() )
302 if( brInfo.tclass && strncmp( brInfo.tclass->GetName(), "SG::PackedContainer<", 20) == 0)
303 brInfo.isPackedContainer = true;
304
305 string elem_tname, branch_tname;
306 // AuxElement TypeID
307 const std::type_info* ti = getAuxElementType( brInfo.tclass, brInfo.edtyp, store.standalone(),
308 elem_tname, branch_tname );
309 const std::type_info* reg_ti = r.getType(auxid);
310 // I/O / Storage TypeID
311 const std::type_info *io_tinf = store.getIOType(auxid);
312 const std::type_info *tcls_tinf = brInfo.tclass ? brInfo.tclass->GetTypeInfo() : ti;
313
314 if (not io_tinf){
316 throw string("Error getting IO type for AUX branch ") + brInfo.branch->GetName();
317 }
318 // if there is a TClass compare the whole storage types (usually vectors), because the Element type
319 // returned by CollProxy loses the pointer component and element type comparison for vector<T*> fails
320 brInfo.needsSE = brInfo.tclass ?
321 io_tinf != tcls_tinf && (!tcls_tinf || strcmp(io_tinf->name(), tcls_tinf->name()) != 0)
322 : ti && ti != reg_ti && strcmp(ti->name(), reg_ti->name()) != 0;
323 if( brInfo.needsSE ) {
324 // type in registry is different than type in the file.
325 // will need to use ROOT auto schema evolution
326 errorcheck::ReportMessage msg (MSG::INFO, ERRORCHECK_ARGS, "TBranchAuxDynReader");
327 msg << "attribute '" << brInfo.attribName << "' (id=" << auxid
328 << " typename=" << SG::normalizedTypeinfoName(*reg_ti)
329 << ") has different type than the branch: " << branch_tname;
330 msg << " Marking for schema evolution.";
331
332 brInfo.SE_tclass = TClass::GetClass(*io_tinf);
333 brInfo.SE_edt = kOther_t;
334 if( !brInfo.SE_tclass ) {
335 brInfo.SE_edt = TDataType::GetType(*io_tinf);
336 if( brInfo.SE_edt <=0 ) {
338 throw string("Error getting ROOT type for AUX branch ") + brInfo.branch->GetName()
339 + " typeinfo=" + io_tinf->name();
340 }
341 }
342 }
344 }
345 return brInfo;
346}
347
348
349void TBranchAuxDynReader::addReaderToObject(void* object, size_t ttree_row, std::recursive_mutex* iomtx)
350{
351 if( m_storeHolderOffset >= 0 ) {
352 auto store_holder = reinterpret_cast<SG::IAuxStoreHolder*>((char*)object + m_storeHolderOffset);
353 bool standalone { store_holder->getStoreType()==SG::IAuxStoreHolder::AST_ObjectStore };
354 if( !m_initialized )
355 init(standalone);
356 store_holder->setStore( new TBranchAuxDynStore(*this, ttree_row, standalone, iomtx) );
357 }
358}
Hold information about an option setting request.
Handle mappings between names and auxid_t.
Exceptions that can be thrown from AthContainers.
char data[hepevt_bytes_allocation_ATLAS]
Definition HepEvt.cxx:11
static Double_t tc
static Double_t rc
Wrapper for ROOT types.
bool addAuxID(const SG::auxid_t &id)
Wrapper for ROOT types.
Definition Type.h:40
An auxiliary data store that holds data internally.
Handle mappings between names and auxid_t.
static std::string linkedName(const std::string &name)
Given a variable name, return the name of the corresponding linked variable.
static bool classNameHasLink(const std::string &className)
Test to see if a class name corresponds to a class with a linked variable.
static AuxTypeRegistry & instance()
Return the singleton registry instance.
Interface for objects taking part in direct ROOT I/O.
@ AST_ObjectStore
The store describes a single object.
TBranchAuxDynReader(TTree *tree, TBranch *base_branch)
SG::auxid_t initBranch(bool standalone, const std::string &attr, TBranch *branch)
std::map< std::string, TBranch * > m_branchMap
virtual void addReaderToObject(void *object, size_t ttree_row, std::recursive_mutex *iomtx=nullptr) override final
Attach specialized AuxStore for reading dynamic attributes.
BranchInfo & getBranchInfo(const SG::auxid_t &auxid, const SG::AuxStoreInternal &store)
std::map< SG::auxid_t, BranchInfo > m_branchInfos
void init(bool standalone)
Helper class to use to report a message.
Helper for emitting error messages.
Find the auxid for a dynamic branch.
int r
Definition globals.cxx:22
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:177
std::string getKeyFromBranch(TBranch *branch)
Exctract the Aux object SG Key from the branch name.
bool removeAuxPostfix(std::string &str)
if a string ends with AUX_POSTFIX then remove it
std::string auxBranchName(const std::string &attr_name, const std::string &baseBranchName)
Construct branch name for a given dynamic attribute.
std::string normalizedTypeinfoName(const std::type_info &info)
Convert a type_info to a normalized string representation (matching the names used in the root dictio...
static const auxid_t null_auxid
To signal no aux data item.
Definition AuxTypes.h:30
SG::auxid_t getDynamicAuxID(const std::type_info &ti, const std::string &name, const std::string &elementTypeName, const std::string &branch_type_name, bool standalone, SG::auxid_t linked_auxid)
Find the auxid for a dynamic branch.
size_t auxid_t
Identifier for a particular aux data item.
Definition AuxTypes.h:27
MsgStream & msg
Definition testRead.cxx:32
TChain * tree