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