ATLAS Offline Software
Loading...
Searching...
No Matches
DataVectorConvert.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
11
12
15#include "TClass.h"
16#include "TBuffer.h"
17#include "TClassEdit.h"
18#include "ESTLType.h"
20#include "TMemberStreamer.h"
21#include "TStreamerElement.h"
22#include "TStreamerInfo.h"
23#include "TRealData.h"
24#include "TDataMember.h"
25#include "TMethodCall.h"
26#include "TInterpreter.h"
27#include "TROOT.h"
28#include <vector>
29#include <sstream>
30#include <cstring>
31#include <algorithm>
32#include <mutex>
33
34
35namespace {
36
37
38//============================================================================
39// Custom streamer class for converting vectors.
40//
41
42
52class DataVectorConvertStreamer
53 : public TMemberStreamer
54{
55public:
65 DataVectorConvertStreamer (TClass* cl, TClass* from, TClass* to);
66
67
74 virtual void operator() (TBuffer& b, void* pmember, Int_t size=0);
75
76
83 void* convert_ptr (void* from);
84
85
86private:
88 TClass* m_cl;
89
91 TClass* m_from;
92
94 TClass* m_to;
95};
96
97
107DataVectorConvertStreamer::DataVectorConvertStreamer (TClass* cl,
108 TClass* from,
109 TClass* to)
110 : m_cl (cl),
111 m_from (from),
112 m_to (to)
113{
114}
115
116
123void
124DataVectorConvertStreamer::operator() (TBuffer& b,
125 void* pmember,
126 Int_t size)
127{
128 // The object to which we're reading, cast to a vector.
129 // Note that the code here assumes that std::vector<X*> has
130 // the same layout for any X. This should be the case for any
131 // sane implementation, but it's not guaranteed. If this causes
132 // a problem, this could be done by first reading into a temporary
133 // and then copying.
134 std::vector<void*>* obj = (std::vector<void*>*)pmember;
135
136 // Loop over instances. In our application, @c size should really
137 // only be 1.
138 while (size-- > 0) {
139 // Read in the object. The pointers will still need to be converted.
140 m_cl->Streamer (obj, b);
141
142 // Loop over each pointer and convert.
143 std::vector<void*>& v = *obj;
144 size_t len = v.size();
145 for (unsigned i=0; i < len; i++)
146 v[i] = convert_ptr (v[i]);
147
148 // Move to the next instance.
149 ++obj;
150 }
151}
152
153
160void* DataVectorConvertStreamer::convert_ptr (void* from)
161{
162 // Don't need to do anything if source and dest classes are the
163 // same, or if this is a null pointer.
164 if (m_from == m_to || from == 0)
165 return from;
166
167 // We used to to the conversion like this.
168 // But this doesn't work in root6 if virtual derivation is used.
169#if 0
170 // We do the conversion by finding the actual (most-derived) class
171 // of this instance, then looking up the base class offsets of the
172 // source and destination classes. In principle, if the source
173 // and destination classes are related by non-virtual inheritance,
174 // then this can reduce to a constant that we could compute at initialization
175 // time. However, i was unable to get information from root on the
176 // inheritance type (virtual vs. non-virtual), so that optimization
177 // is not done.
178 TClass* cl_actual = m_from->GetActualClass (from);
179 if (!cl_actual)
180 return 0;
181 int offs1 = cl_actual->GetBaseClassOffset (m_from);
182 int offs2 = cl_actual->GetBaseClassOffset (m_to);
183 if (offs1 < 0 || offs2 < 0)
184 return 0;
185 return (char*)from - offs1 + offs2;
186#endif
187
188 // So instead, just call to the interpreter.
189 // Slow, but doesn't matter so much here since this is just
190 // for backwards compatibility.
191 std::ostringstream os;
192 os << "dynamic_cast<" << m_to->GetName() << "*>((" << m_from->GetName() << "*)" << from << ")";
193 std::string line = os.str();
194 Long_t ret = gInterpreter->Calc (line.c_str());
195 return reinterpret_cast<void*> (ret);
196}
197
198
199//============================================================================
200// Helper functions for installing the conversion.
201//
202
203
219TClass* find_class (RootUtils::ILogger* logfn,
220 TClass* dv_class,
221 const std::string& name,
222 bool inner = false)
223{
224 std::string name2 = name;
225 {
226 // Protect against data race inside TClassEdit.
227 // https://github.com/root-project/root/issues/10353
228 // Should be fixed in root 6.26.02.
229 R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
230 if (inner)
231 name2 = TClassEdit::ShortType (name2.c_str(), TClassEdit::kInnerClass);
232 name2 = TClassEdit::ShortType (name2.c_str(), TClassEdit::kDropTrailStar);
233 }
234 TClass* cls = gROOT->GetClass (name2.c_str());
235 if (cls == 0 && logfn) {
236 std::ostringstream ss;
237 ss << dv_class->GetName() << ": Can't find class " << name2;
238 logfn->debug (ss.str().c_str());
239 }
240 return cls;
241}
242
243
254int get_member_offset (TClass* dv_class, TDataMember* dm)
255{
256 TMethodCall meth;
257 meth.InitWithPrototype (dv_class, "baseOffset", "const std::type_info&");
258 if (!meth.IsValid()) {
259 return -1;
260 }
261 TClass* mcls = dm->GetClass();
262 meth.ResetParam();
263 meth.SetParam (reinterpret_cast<Long_t> (mcls->GetTypeInfo()));
264 Long_t ret = 0;
265 meth.Execute (ret);
266 TRealData* rd = mcls->GetRealData (dm->GetName());
267 if (rd)
268 return rd->GetThisOffset() + ret;
269 return -1;
270}
271
272
286void diddle_dv_streaminfo (RootUtils::ILogger* logfn,
287 TClass* dv_class,
288 TStreamerInfo* si1,
289 TStreamerElement* sie0,
290 TStreamerElement* sie1)
291{
292 // This is the type from which we need to convert.
293 // Give up if it ain't a vector.
294 std::string vec_name = sie1->GetTypeName();
295 bool isvec = true;
296 {
297 // Protect against data race inside TClassEdit.
298 // https://github.com/root-project/root/issues/10353
299 // Should be fixed in root 6.26.02.
300 R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
301 if (std::abs(TClassEdit::IsSTLCont (vec_name.c_str(), 0)) !=
302 ROOT::kSTLvector)
303 {
304 isvec = false;
305 }
306 }
307 if (!isvec)
308 {
309 if (logfn) {
310 std::ostringstream ss;
311 ss << dv_class->GetName() << ": Type to be converted is not a vector: "
312 << vec_name;
313 logfn->debug (ss.str().c_str());
314 }
315 return;
316 }
317
318 // Extract the inner classes from the source and target vector names,
319 // as well as the source vector itself. Give up if any of them
320 // aren't known to root.
321 TClass* cl_from = find_class (logfn, dv_class, vec_name, true);
322 TClass* cl_to = find_class (logfn, dv_class, sie0->GetTypeName(), true);
323 TClass* vcls = find_class (logfn, dv_class, vec_name);
324 if (cl_from == 0 || cl_to == 0 || vcls == 0)
325 return;
326
327 // Fix up the streaminfo. Change the type name to what's expected,
328 // and install a custom streamer.
329 sie1->SetTypeName (sie0->GetTypeName());
330 sie1->TStreamerElement::SetStreamer
331 (new DataVectorConvertStreamer (vcls, cl_from, cl_to));
332
333 // Now we have to make sure the offsets are correct.
334 TIter next (si1->GetElements());
335 while (TStreamerElement* elem = dynamic_cast<TStreamerElement*> (next())) {
336 elem->SetNewType (elem->GetType());
337 TRealData* rd = dv_class->GetRealData (elem->GetName());
338 if (rd) {
339 // We used to do this; however this doesn't work with
340 // root6 if there's virtual derivation (root6 will quietly
341 // return garbage in such a case). We have to get the offsets
342 // in a more roundabout way.
343 //elem->SetOffset (rd->GetThisOffset());
344 int offs = get_member_offset (dv_class, rd->GetDataMember());
345 if (offs >= 0)
346 elem->SetOffset (offs);
347 else
348 elem->SetNewType (-2);
349 }
350 else
351 elem->SetNewType (-2);
352 }
353
354 // Rebuild all the internal tables, suppressing a useless error we see
355 // with more recent ROOT versions.
356 auto errhand = [](int /*level*/, bool /*abort*/, const char* /*location*/, const char* msg)
357 {
358 if (strstr (msg, "No information on the alignment") != 0) {
359 return false;
360 }
361 return true;
362 };
363 RootUtils::WithRootErrorHandler eh (errhand);
364 si1->Compile();
365}
366
367
377void test_dv (RootUtils::ILogger* logfn,
378 TClass* dv_class)
379{
380 if (logfn) {
381 std::ostringstream ss;
382 ss << "Testing class " << dv_class->GetName()
383 << " for possible back-compatibility conversions.";
384 logfn->debug (ss.str().c_str());
385 }
386
387 // Get the array of TStreamerInfos.
388 const TObjArray* infos = dv_class->GetStreamerInfos();
389 if (infos == 0) {
390 if (logfn) {
391 std::ostringstream ss;
392 ss << dv_class->GetName() << ": Can't find TStreamerInfo list.";
393 logfn->debug (ss.str().c_str());
394 }
395 return;
396 }
397
398 // If no streaminfos, this class isn't involved in persistency. Skip it.
399 if (infos->GetEntries() == 0)
400 return;
401
402 // Get the class version, and thence the primary class shape.
403 int dv_vers = dv_class->GetClassVersion();
404 TVirtualStreamerInfo* si0 = dv_class->GetStreamerInfo();
405 if (si0 == 0) {
406 if (logfn) {
407 std::ostringstream ss;
408 ss << dv_class->GetName() << ": Can't find primary shape.";
409 logfn->debug (ss.str().c_str());
410 }
411 return;
412 }
413
414 // Look up the m_pCont field in the primary shape.
415 Int_t offset;
416 TStreamerElement* sie0 = si0->GetStreamerElement ("m_pCont", offset);
417 bool isvec = (sie0 != 0);
418 if (isvec) {
419 // Protect against data race inside TClassEdit.
420 // https://github.com/root-project/root/issues/10353
421 // Should be fixed in root 6.26.02.
422 R__WRITE_LOCKGUARD(ROOT::gCoreMutex);
423 if (std::abs(TClassEdit::IsSTLCont (sie0->GetTypeName(), 0)) !=
424 ROOT::kSTLvector)
425 {
426 isvec = false;
427 }
428 }
429 if (!isvec)
430 {
431 if (logfn) {
432 std::ostringstream ss;
433 ss << dv_class->GetName() << ": m_pCont not present, or not a vector.";
434 logfn->debug (ss.str().c_str());
435 }
436 return;
437 }
438
439 // Go through all other shapes.
440 // Look up m_pCont in each.
441 // If the type differs from that in the primary shape, then we'll
442 // need to fix up the streamer info.
443 Int_t nent = infos->GetEntriesFast();
444 for (Int_t i = 0; i < nent; i++) {
445 if (i == dv_vers) continue;
446 TStreamerInfo* si1 = dynamic_cast<TStreamerInfo*>(infos->At (i));
447 if (si1 != 0) {
448 TStreamerElement* sie1 = si1->GetStreamerElement ("m_pCont", offset);
449 if (sie1 != 0 && strcmp (sie0->GetTypeName(), sie1->GetTypeName()) != 0){
450 if (logfn) {
451 std::ostringstream ss;
452 ss << dv_class->GetName()
453 << ": SI slot " << i << " has m_pCont with type "
454 << sie1->GetTypeName() << "; needs adjustment.";
455 logfn->debug (ss.str().c_str());
456 }
457 diddle_dv_streaminfo (logfn, dv_class, si1, sie0, sie1);
458 }
459 }
460 }
461}
462
463
464} // anonymous namespace
465
466
467namespace DataModelAthenaPool {
468
469
481{
482 static std::mutex m;
483 std::lock_guard<std::mutex> lock (m);
484
485 // List of classes we've already processed.
486 static std::vector<TClass*> s_seen ATLAS_THREAD_SAFE;
487
488 TIter next (gROOT->GetListOfClasses());
489 while (TClass* cls = dynamic_cast<TClass*> (next())) {
490 if (std::strncmp (cls->GetName(), "DataVector<", 11) == 0) {
491 if (std::find (s_seen.begin(), s_seen.end(), cls) == s_seen.end()) {
492 test_dv (logfn, cls);
493 s_seen.push_back (cls);
494 }
495 }
496 }
497}
498
499
500} // namespace DataModelAthenaPool
Provide backwards compatibility for reading DataVector classes.
virtual void lock()=0
Interface to allow an object to lock itself when made const in SG.
static Double_t ss
void operator()(T1)
size_t size() const
Number of registered mappings.
Run a MT piece of code with an alternate root error handler.
Define macros for attributes used to control the static checker.
#define ATLAS_THREAD_SAFE
static void initialize(RootUtils::ILogger *logfn=0)
Scan all known DataVector classes and fix up the Root data for any that need conversion.
Very simple interface for writing log messages.
Definition ILogger.h:34
virtual void debug(const char *msg)=0
Log a debugging message.
CONT to(RANGE &&r)
Definition ranges.h:39
cl
print [x.__class__ for x in toList(dqregion.getSubRegions()) ]
MsgStream & msg
Definition testRead.cxx:32