Logo ROOT   6.12/07
Reference Guide
TCheckHashRecursiveRemoveConsistency.h
Go to the documentation of this file.
1 // @(#)root/meta:$Id$
2 // Author: Rene Brun 07/01/95
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #ifndef ROOT_TCheckHashRecursiveRemoveConsistency
13 #define ROOT_TCheckHashRecursiveRemoveConsistency
14 
15 #include "TBaseClass.h"
16 #include "TClass.h"
17 #include "TError.h"
18 #include "TMethod.h"
19 #include "TROOT.h"
20 
21 #include <list>
22 
23 #include <iostream>
24 
25 //////////////////////////////////////////////////////////////////////////
26 // //
27 // TCheckHashRecursiveRemoveConsistency //
28 // //
29 // Utility class to discover whether a class that overload //
30 // TObject::Hash also (as required) calls RecursiveRemove in its //
31 // destructor. //
32 // //
33 //////////////////////////////////////////////////////////////////////////
34 
35 namespace ROOT {
36 namespace Internal {
37 
39 public:
40  struct Value {
43  };
44  using Value_t = Value; // std::pair<ULong_t, TObject*>;
45 
46  std::list<Value> fCont;
47 
48 public:
49  // Default constructor. Adds object to the list of
50  // cleanups.
52  {
54  gROOT->GetListOfCleanups()->Add(this);
55  }
56 
57  // Destructor. This class does not overload
58  // Hash so it can rely on the base class to call
59  // RecursiveRemove (and hence remove this from the list
60  // of cleanups).
62  {
63  // ... unless the mechanism is disabled in which case
64  // we need to do it explicitly.
65  if (!gROOT->MustClean())
66  gROOT->GetListOfCleanups()->Remove(this);
67  }
68 
69  void Add(TObject *obj)
70  {
71  obj->SetBit(kMustCleanup);
72  fCont.push_back(Value_t{obj->Hash(), obj});
73  }
74 
76  {
77  // std::cout << "Recursive Remove called for: " << obj << '\n';
78  for (auto p = fCont.begin(); p != fCont.end(); ++p) {
79  if (p->fObjectPtr == obj) {
80  // std::cout << " Found object with hash = " << p->fRecordedHash << '\n';
81  // std::cout << " Current hash = " << obj->Hash() << '\n';
82  if (p->fRecordedHash == obj->Hash())
83  fCont.erase(p);
84  // else
85  // std::cout << " Error: the recorded hash and the one returned by Hash are distinct.\n";
86  break;
87  }
88  }
89  }
90 
91  void SlowRemove(TObject *obj)
92  {
93  for (auto p = fCont.begin(); p != fCont.end(); ++p) {
94  if (p->fObjectPtr == obj) {
95  fCont.erase(p);
96  break;
97  }
98  }
99  }
100 
101  enum EResult {
105  };
106 
108  {
109  if (!classRef.HasDefaultConstructor() || classRef.Property() & kIsAbstract)
110  return kInconclusive; // okay that's probably a false negative ...
111 
112  auto size = fCont.size();
113  TObject *obj = (TObject *)classRef.DynamicCast(TObject::Class(), classRef.New(TClass::kDummyNew));
114  if (!obj || (!gROOT->MustClean() && obj->TestBit(kIsReferenced) && obj->GetUniqueID() != 0)) {
115  // Clean up is disable and the object is such that we wont be able to 'mark' it
116  // as needing a clean up anyway, so we can not actually test it.
117  return kInconclusive;
118  }
120  Add(obj);
121  delete obj;
122 
123  if (fCont.size() != size) {
124  // std::cerr << "Error: old= " << size << " new=" << fCont.size() << '\n';
125  // std::cerr << "Error " << classRef.GetName() <<
126  // " or one of its base classes override TObject::Hash but does not call TROOT::CallRecursiveRemoveIfNeeded
127  // in its destructor.\n";
128  SlowRemove(obj);
129  return kInconsistent;
130  } else {
131  return kConsistent;
132  }
133  }
134 
136  {
137 
138  if (classRef.HasLocalHashMember() && CheckRecursiveRemove(classRef) != kConsistent) {
139  return &classRef;
140  }
141 
142  for (auto base : ROOT::Detail::TRangeStaticCast<TBaseClass>(classRef.GetListOfBases())) {
143  TClass *baseCl = base->GetClassPointer();
144  TClass *res = FindMissingRecursiveRemove(*baseCl);
145  if (res)
146  return res;
147  }
148  return nullptr;
149  }
150 
151  bool VerifyRecursiveRemove(const char *classname)
152  {
153  TClass *classPtr = TClass::GetClass(classname);
154  if (classPtr)
155  return VerifyRecursiveRemove(*classPtr);
156  else
157  return true;
158  }
159 
161  {
162  // Use except if the class is non-default/abstract and HasLocalHashMember.
163  if (classRef.fRuntimeProperties) {
164  // We already did this testing for this class.
165  return classRef.HasConsistentHashMember() ? kConsistent : kInconsistent;
166  }
167 
168  if (classRef.HasLocalHashMember())
169  return CheckRecursiveRemove(classRef);
170 
171  EResult baseResult = kConsistent;
172  for (auto base : ROOT::Detail::TRangeStaticCast<TBaseClass>(classRef.GetListOfBases())) {
173  TClass *baseCl = base->GetClassPointer();
174 
175  if (baseCl->HasLocalHashMember() &&
176  (!baseCl->HasDefaultConstructor() || baseCl->Property() & kIsAbstract))
177  {
178  // We won't be able to check the base class, we need to (try) to check
179  // this class even-though it does not have a local HashMember.
180  return CheckRecursiveRemove(classRef);
181  }
182  auto baseConsistency = HasConsistentHashMember(*baseCl);
183  if (baseConsistency == kInconsistent) {
184  baseResult = kInconsistent;
185  } else if (baseConsistency == kInconclusive) {
186  return CheckRecursiveRemove(classRef);
187  }
188  }
189  return baseResult;
190  }
191 
193  {
194  // If the class does not inherit from TObject, the setup is always 'correct'
195  // (or more exactly does not matter).
196  if (!classRef.IsTObject())
197  return true;
198 
199  if (classRef.HasLocalHashMember() &&
200  (!classRef.HasDefaultConstructor() || classRef.Property() & kIsAbstract))
201  // We won't be able to check, so assume the worst but don't issue any
202  // error message.
203  return false;
204 
205  if (HasConsistentHashMember(classRef) != kConsistent) {
206  TClass *failing = FindMissingRecursiveRemove(classRef);
207 
208  // Because ClassDefInline does not yet support class template on all platforms,
209  // we have no ClassDef and thus can not get a good message from TObject::Error.
210  constexpr const char *funcName = "ROOT::Internal::TCheckHashRecursiveRemoveConsistency::CheckRecursiveRemove";
211  if (failing) {
212  ::Error(funcName,
213  "The class %s overrides TObject::Hash but does not call TROOT::RecursiveRemove in its destructor (seen while checking %s).",
214  failing->GetName(),classRef.GetName());
215  } else {
216  ::Error(funcName, "The class %s "
217  "or one of its base classes override TObject::Hash but does not call "
218  "TROOT::CallRecursiveRemoveIfNeeded in its destructor.\n",
219  classRef.GetName());
220  }
221  return false;
222  }
223  return true;
224  }
225 
226  static bool Check(TClass &classRef)
227  {
229  return checker.VerifyRecursiveRemove(classRef);
230  }
231 
233 };
234 
235 } // namespace Internal
236 } // namespace ROOT
237 
238 #endif // ROOT__TCheckHashRecursiveRemoveConsistency
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
virtual UInt_t GetUniqueID() const
Return the unique object id.
Definition: TObject.cxx:375
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition: TClass.cxx:3507
virtual ULong_t Hash() const
Return hash value for this object.
Definition: TObject.cxx:433
Namespace for new ROOT classes and functions.
Definition: StringConv.hxx:21
#define gROOT
Definition: TROOT.h:402
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition: TObject.h:172
TRangeStaticCast is an adaptater class that allows the typed iteration through a TCollection.
Definition: TCollection.h:380
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition: TObject.cxx:694
void Class()
Definition: Class.C:29
Bool_t HasLocalHashMember() const
Bool_t HasDefaultConstructor() const
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:880
The ROOT global object gROOT contains a list of all defined classes.
Definition: TClass.h:75
std::atomic< UChar_t > fRuntimeProperties
Definition: TClass.h:238
Long_t Property() const
Set TObject::fBits and fStreamerType to cache information about the class.
Definition: TClass.cxx:5768
if object destructor must call RecursiveRemove()
Definition: TObject.h:60
Bool_t HasConsistentHashMember()
Return &#39;true&#39; if we can guarantee that if this class (or any class in this class inheritance hierarch...
Definition: TClass.h:466
ClassDefInline(TCheckHashRecursiveRemoveConsistency, 0)
unsigned long ULong_t
Definition: RtypesCore.h:51
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition: TClass.cxx:2887
Mother of all ROOT objects.
Definition: TObject.h:37
Bool_t IsTObject() const
Return kTRUE is the class inherits from TObject.
Definition: TClass.cxx:5668
void RecursiveRemove(TObject *obj)
Recursively remove this object from a list.
if object is referenced by a TRef or TRefArray
Definition: TObject.h:61
void * DynamicCast(const TClass *base, void *obj, Bool_t up=kTRUE)
Cast obj of this class type up to baseclass cl if up is true.
Definition: TClass.cxx:4729
void SetRequireCleanup(TObject &obj)
Definition: TROOT.h:377
const char * Value
Definition: TXMLSetup.cxx:72
void * New(ENewType defConstructor=kClassNew, Bool_t quiet=kFALSE) const
Return a pointer to a newly allocated object of this class.
Definition: TClass.cxx:4792