ATLAS Offline Software
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 
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 
33 namespace {
34 
35 
36 //============================================================================
37 // Custom streamer class for converting vectors.
38 //
39 
40 
50 class DataVectorConvertStreamer
51  : public TMemberStreamer
52 {
53 public:
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 
84 private:
86  TClass* m_cl;
87 
89  TClass* m_from;
90 
92  TClass* m_to;
93 };
94 
95 
105 DataVectorConvertStreamer::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 
121 void
122 DataVectorConvertStreamer::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 
158 void* 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 
217 TClass* 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 
252 int 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 
284 void 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 
366 void 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 
456 namespace 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
checkFileSG.line
line
Definition: checkFileSG.py:75
python.SystemOfUnits.m
int m
Definition: SystemOfUnits.py:91
PowhegControl_ttHplus_NLO.ss
ss
Definition: PowhegControl_ttHplus_NLO.py:83
find
std::string find(const std::string &s)
return a remapped string
Definition: hcg.cxx:135
BeamSpot::mutex
std::mutex mutex
Definition: InDetBeamSpotVertex.cxx:18
DataModelAthenaPool
Definition: CLHEPConverters.h:20
CaloClusterListBadChannel.cls
cls
Definition: CaloClusterListBadChannel.py:8
DataModelAthenaPool::DataVectorConvert::initialize
static void initialize(RootUtils::ILogger *logfn=0)
Scan all known DataVector classes and fix up the Root data for any that need conversion.
Definition: DataVectorConvert.cxx:469
python.setupRTTAlg.size
int size
Definition: setupRTTAlg.py:39
fillPileUpNoiseLumi.next
next
Definition: fillPileUpNoiseLumi.py:52
lumiFormat.i
int i
Definition: lumiFormat.py:92
ret
T ret(T t)
Definition: rootspy.cxx:260
RootUtils::ILogger::debug
virtual void debug(const char *msg)=0
Log a debugging message.
ReadFromCoolCompare.os
os
Definition: ReadFromCoolCompare.py:231
ReadCellNoiseFromCool.dm
dm
Definition: ReadCellNoiseFromCool.py:235
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:195
plotBeamSpotMon.b
b
Definition: plotBeamSpotMon.py:77
CxxUtils::to
CONT to(RANGE &&r)
Definition: ranges.h:32
DataVectorConvert.h
Provide backwards compatibility for reading DataVector classes.
RootUtils::ILogger
Very simple interface for writing log messages.
Definition: ILogger.h:34
python.PyAthena.v
v
Definition: PyAthena.py:157
convertTimingResiduals.offset
offset
Definition: convertTimingResiduals.py:71
ATLAS_THREAD_SAFE
#define ATLAS_THREAD_SAFE
Definition: checker_macros.h:211
checker_macros.h
Define macros for attributes used to control the static checker.
python.PyAthena.obj
obj
Definition: PyAthena.py:135
dq_make_web_display.cl
cl
print [x.__class__ for x in toList(dqregion.getSubRegions()) ]
Definition: dq_make_web_display.py:26