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