Logo ROOT  
Reference Guide
TClassEdit.cxx
Go to the documentation of this file.
1 // @(#)root/metautils:$Id$
2 /// \file TClassEdit.cxx
3 /// \ingroup Base
4 /// \author Victor Perev
5 /// \author Philippe Canal
6 /// \date 04/10/2003
7 
8 /*************************************************************************
9  * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
10  * All rights reserved. *
11  * *
12  * For the licensing terms see $ROOTSYS/LICENSE. *
13  * For the list of contributors see $ROOTSYS/README/CREDITS. *
14  *************************************************************************/
15 
16 #include <cstdio>
17 #include <cstdlib>
18 #include <cassert>
19 #include <cstring>
20 #include "TClassEdit.h"
21 #include <cctype>
22 #include "Rstrstream.h"
23 #include <set>
24 #include <stack>
25 // for shared_ptr
26 #include <memory>
27 #include "ROOT/RStringView.hxx"
28 #include <algorithm>
29 
30 using namespace std;
31 
32 namespace {
33  static TClassEdit::TInterpreterLookupHelper *gInterpreterHelper = nullptr;
34 
35  template <typename T>
36  struct ShuttingDownSignaler : public T {
37  using T::T;
38 
39  ~ShuttingDownSignaler()
40  {
41  if (gInterpreterHelper)
42  gInterpreterHelper->ShuttingDownSignal();
43  }
44  };
45 }
46 
47 ////////////////////////////////////////////////////////////////////////////////
48 /// Return the length, if any, taken by std:: and any
49 /// potential inline namespace (well compiler detail namespace).
50 
51 static size_t StdLen(const std::string_view name)
52 {
53  size_t len = 0;
54  if (name.compare(0,5,"std::")==0) {
55  len = 5;
56 
57  // TODO: This is likely to induce unwanted autoparsing, those are reduced
58  // by the caching of the result.
59  if (gInterpreterHelper) {
60  for(size_t i = 5; i < name.length(); ++i) {
61  if (name[i] == '<') break;
62  if (name[i] == ':') {
63  bool isInlined;
64  std::string scope(name.data(),i);
65  std::string scoperesult;
66  // We assume that we are called in already serialized code.
67  // Note: should we also cache the negative answers?
68  static ShuttingDownSignaler<std::set<std::string>> gInlined;
69 
70  if (gInlined.find(scope) != gInlined.end()) {
71  len = i;
72  if (i+1<name.length() && name[i+1]==':') {
73  len += 2;
74  }
75  }
76  if (!gInterpreterHelper->ExistingTypeCheck(scope, scoperesult)
77  && gInterpreterHelper->IsDeclaredScope(scope,isInlined)) {
78  if (isInlined) {
79  gInlined.insert(scope);
80  len = i;
81  if (i+1<name.length() && name[i+1]==':') {
82  len += 2;
83  }
84  }
85  }
86  }
87  }
88  }
89  }
90 
91  return len;
92 }
93 
94 ////////////////////////////////////////////////////////////////////////////////
95 /// Remove std:: and any potential inline namespace (well compiler detail
96 /// namespace.
97 
98 static void RemoveStd(std::string &name, size_t pos = 0)
99 {
100  size_t len = StdLen({name.data()+pos,name.length()-pos});
101  if (len) {
102  name.erase(pos,len);
103  }
104 }
105 
106 ////////////////////////////////////////////////////////////////////////////////
107 /// Remove std:: and any potential inline namespace (well compiler detail
108 /// namespace.
109 
111 {
112  size_t len = StdLen(name);
113  if (len) {
114  name.remove_prefix(len);
115  }
116 }
117 
118 ////////////////////////////////////////////////////////////////////////////////
119 
121 {
122  if (0 == strncmp(clName, "complex<", 8)) {
123  const char *clNamePlus8 = clName + 8;
124  if (0 == strcmp("float>", clNamePlus8)) {
125  return EComplexType::kFloat;
126  }
127  if (0 == strcmp("double>", clNamePlus8)) {
128  return EComplexType::kDouble;
129  }
130  if (0 == strcmp("int>", clNamePlus8)) {
131  return EComplexType::kInt;
132  }
133  if (0 == strcmp("long>", clNamePlus8)) {
134  return EComplexType::kLong;
135  }
136  }
137  return EComplexType::kNone;
138 }
139 
140 ////////////////////////////////////////////////////////////////////////////////
142 {
143  // Already too late to call this->ShuttingDownSignal
144  // the virtual table has already lost (on some platform) the
145  // address of the derived function that we would need to call.
146  // But at least forget about this instance!
147 
148  if (this == gInterpreterHelper)
149  gInterpreterHelper = nullptr;
150 }
151 
152 ////////////////////////////////////////////////////////////////////////////////
153 
155 {
156  gInterpreterHelper = helper;
157 }
158 
159 ////////////////////////////////////////////////////////////////////////////////
160 /// default constructor
161 
162 TClassEdit::TSplitType::TSplitType(const char *type2split, EModType mode) : fName(type2split), fNestedLocation(0)
163 {
164  TClassEdit::GetSplit(type2split, fElements, fNestedLocation, mode);
165 }
166 
167 ////////////////////////////////////////////////////////////////////////////////
168 /// type : type name: vector<list<classA,allocator>,allocator>[::iterator]
169 /// result: 0 : not stl container and not declared inside an stl container.
170 /// result: code of container that the type or is the scope of the type
171 
173 {
174  if (fElements[0].empty()) return ROOT::kNotSTL;
175  return STLKind(fElements[0]);
176 }
177 
178 ////////////////////////////////////////////////////////////////////////////////
179 /// type : type name: vector<list<classA,allocator>,allocator>
180 /// testAlloc: if true, we test allocator, if it is not default result is negative
181 /// result: 0 : not stl container
182 /// abs(result): code of container 1=vector,2=list,3=deque,4=map
183 /// 5=multimap,6=set,7=multiset
184 /// positive val: we have a vector or list with default allocator to any depth
185 /// like vector<list<vector<int>>>
186 /// negative val: STL container other than vector or list, or non default allocator
187 /// For example: vector<deque<int>> has answer -1
188 
189 int TClassEdit::TSplitType::IsSTLCont(int testAlloc) const
190 {
191 
192  if (fElements[0].empty()) return 0;
193  int numb = fElements.size();
194  if (!fElements[numb-1].empty() && fElements[numb-1][0]=='*') --numb;
195 
196  if ( fNestedLocation ) {
197  // The type has been defined inside another namespace and/or class
198  // this couldn't possibly be an STL container
199  return 0;
200  }
201 
202  int kind = STLKind(fElements[0]);
203 
204  if (kind==ROOT::kSTLvector || kind==ROOT::kSTLlist || kind==ROOT::kSTLforwardlist) {
205 
206  int nargs = STLArgs(kind);
207  if (testAlloc && (numb-1 > nargs) && !IsDefAlloc(fElements[numb-1].c_str(),fElements[1].c_str())) {
208 
209  // We have a non default allocator,
210  // let's return a negative value.
211 
212  kind = -kind;
213 
214  } else {
215 
216  // We has a default allocator, let's continue to
217  // look inside the argument list.
218  int k = TClassEdit::IsSTLCont(fElements[1].c_str(),testAlloc);
219  if (k<0) kind = -kind;
220 
221  }
222  }
223 
224  // We return a negative value for anything which is not a vector or a list.
225  if(kind>2) kind = - kind;
226  return kind;
227 }
228 
229 ////////////////////////////////////////////////////////////////////////////////
230 //////////////////////////////////////////////////////////////////////////////
231 /// Return the absolute type of typeDesc into the string answ.
232 
233 void TClassEdit::TSplitType::ShortType(std::string &answ, int mode)
234 {
235  // E.g.: typeDesc = "class const volatile TNamed**", returns "TNamed**".
236  // if (mode&1) remove last "*"s returns "TNamed"
237  // if (mode&2) remove default allocators from STL containers
238  // if (mode&4) remove all allocators from STL containers
239  // if (mode&8) return inner class of stl container. list<innerClass>
240  // if (mode&16) return deepest class of stl container. vector<list<deepest>>
241  // if (mode&kDropAllDefault) remove default template arguments
242  /////////////////////////////////////////////////////////////////////////////
243 
244  answ.clear();
245  int narg = fElements.size();
246  int tailLoc = 0;
247 
248  if (narg == 0) {
249  answ = fName;
250  return ;
251  }
252  // fprintf(stderr,"calling ShortType %d for %s with narg %d\n",mode,typeDesc,narg);
253  // {for (int i=0;i<narg;i++) fprintf(stderr,"calling ShortType %d for %s with %d %s \n",
254  // mode,typeDesc,i,arglist[i].c_str());
255  // }
256  if (fElements[narg-1].empty() == false &&
257  (fElements[narg-1][0]=='*'
258  || fElements[narg-1][0]=='&'
259  || fElements[narg-1][0]=='['
260  || 0 == fElements[narg-1].compare(0,6,"const*")
261  || 0 == fElements[narg-1].compare(0,6,"const&")
262  || 0 == fElements[narg-1].compare(0,6,"const[")
263  || 0 == fElements[narg-1].compare("const")
264  )
265  ) {
266  if ((mode&1)==0) tailLoc = narg-1;
267  }
268  else { assert(fElements[narg-1].empty()); };
269  narg--;
270  mode &= (~1);
271 
272  if (fNestedLocation) narg--;
273 
274  // fprintf(stderr,"calling ShortType %d for %s with narg %d tail %d\n",imode,typeDesc,narg,tailLoc);
275 
276  //kind of stl container
277  const int kind = STLKind(fElements[0]);
278  const int iall = STLArgs(kind);
279 
280  // Only class is needed
281  if (mode&(8|16)) {
282  while(narg-1>iall) { fElements.pop_back(); narg--;}
283  if (!fElements[0].empty() && tailLoc) {
284  tailLoc = 0;
285  }
286  fElements[0].clear();
287  mode&=(~8);
288  }
289 
290  if (mode & kDropAllDefault) mode |= kDropStlDefault;
291  if (mode & kDropStlDefault) mode |= kDropDefaultAlloc;
292 
293  if (kind) {
294  bool allocRemoved = false;
295 
296  if ( mode & (kDropDefaultAlloc|kDropAlloc) ) {
297  // remove allocators
298 
299 
300  if (narg-1 == iall+1) {
301  // has an allocator specified
302  bool dropAlloc = false;
303  if (mode & kDropAlloc) {
304 
305  dropAlloc = true;
306 
307  } else if (mode & kDropDefaultAlloc) {
308  switch (kind) {
309  case ROOT::kSTLvector:
310  case ROOT::kSTLlist:
312  case ROOT::kSTLdeque:
313  case ROOT::kSTLset:
314  case ROOT::kSTLmultiset:
317  dropAlloc = IsDefAlloc(fElements[iall+1].c_str(),fElements[1].c_str());
318  break;
319  case ROOT::kSTLmap:
320  case ROOT::kSTLmultimap:
323  dropAlloc = IsDefAlloc(fElements[iall+1].c_str(),fElements[1].c_str(),fElements[2].c_str());
324  break;
325  default:
326  dropAlloc = false;
327  }
328 
329  }
330  if (dropAlloc) {
331  narg--;
332  allocRemoved = true;
333  }
334  } else {
335  // has no allocator specified (hence it is already removed!)
336  allocRemoved = true;
337  }
338  }
339 
340  if ( allocRemoved && (mode & kDropStlDefault) && narg-1 == iall) { // remove default comparator
341  if ( IsDefComp( fElements[iall].c_str(), fElements[1].c_str() ) ) {
342  narg--;
343  }
344  } else if ( mode & kDropComparator ) {
345 
346  switch (kind) {
347  case ROOT::kSTLvector:
348  case ROOT::kSTLlist:
350  case ROOT::kSTLdeque:
351  break;
352  case ROOT::kSTLset:
353  case ROOT::kSTLmultiset:
354  case ROOT::kSTLmap:
355  case ROOT::kSTLmultimap:
356  if (!allocRemoved && narg-1 == iall+1) {
357  narg--;
358  allocRemoved = true;
359  }
360  if (narg-1 == iall) narg--;
361  break;
362  default:
363  break;
364  }
365  }
366 
367  // Treat now Pred and Hash for unordered set/map containers. Signature is:
368  // template < class Key,
369  // class Hash = hash<Key>,
370  // class Pred = equal_to<Key>,
371  // class Alloc = allocator<Key>
372  // > class unordered_{set,multiset}
373  // template < class Key,
374  // class Val,
375  // class Hash = hash<Key>,
376  // class Pred = equal_to<Key>,
377  // class Alloc = allocator<Key>
378  // > class unordered_{map,multimap}
379 
380 
382 
383  bool predRemoved = false;
384 
385  if ( allocRemoved && (mode & kDropStlDefault) && narg-1 == iall) { // remove default predicate
386  if ( IsDefPred( fElements[iall].c_str(), fElements[1].c_str() ) ) {
387  predRemoved=true;
388  narg--;
389  }
390  }
391 
392  if ( predRemoved && (mode & kDropStlDefault) && narg == iall) { // remove default hash
393  if ( IsDefHash( fElements[iall-1].c_str(), fElements[1].c_str() ) ) {
394  narg--;
395  }
396  }
397  }
398  } // End of treatment of stl containers
399  else {
400  if ( (mode & kDropStlDefault) && (narg >= 3)) {
401  unsigned int offset = (0==strncmp("const ",fElements[0].c_str(),6)) ? 6 : 0;
402  offset += (0==strncmp("std::",fElements[0].c_str()+offset,5)) ? 5 : 0;
403  if (0 == strcmp(fElements[0].c_str()+offset,"__shared_ptr"))
404  {
405 #ifdef _CONCURRENCE_H
406  static const std::string sharedPtrDef = std::to_string(__gnu_cxx::__default_lock_policy); // to_string is C++11
407 #else
408  static const std::string sharedPtrDef = std::to_string(2); // to_string is C++11
409 #endif
410  if (fElements[2] == sharedPtrDef) {
411  narg--;
412  }
413  }
414  }
415  }
416 
417  // do the same for all inside
418  for (int i=1;i<narg; i++) {
419  if (strchr(fElements[i].c_str(),'<')==0) {
420  if (mode&kDropStd) {
421  unsigned int offset = (0==strncmp("const ",fElements[i].c_str(),6)) ? 6 : 0;
422  RemoveStd( fElements[i], offset );
423  }
424  if (mode&kResolveTypedef) {
425  fElements[i] = ResolveTypedef(fElements[i].c_str(),true);
426  }
427  continue;
428  }
429  fElements[i] = TClassEdit::ShortType(fElements[i].c_str(),mode | TClassEdit::kKeepOuterConst);
430  if (mode&kResolveTypedef) {
431  // We 'just' need to check whether the outer type is a typedef or not;
432  // this also will add the default template parameter if any needs to
433  // be added.
434  string typeresult;
435  if (gInterpreterHelper &&
436  (gInterpreterHelper->ExistingTypeCheck(fElements[i], typeresult)
437  || gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(fElements[i], typeresult))) {
438  if (!typeresult.empty()) fElements[i] = typeresult;
439  }
440  }
441  }
442 
443  unsigned int tailOffset = 0;
444  if (tailLoc && fElements[tailLoc].compare(0,5,"const") == 0) {
445  if (mode & kKeepOuterConst) answ += "const ";
446  tailOffset = 5;
447  }
448  if (!fElements[0].empty()) {answ += fElements[0]; answ +="<";}
449 
450 #if 0
451  // This code is no longer use, the moral equivalent would be to get
452  // the 'fixed' number of argument the user told us to ignore and drop those.
453  // However, the name we get here might be (usually) normalized enough that
454  // this is not necessary (at the very least nothing break in roottest without
455  // the aforementioned new code or this old code).
456  if (mode & kDropAllDefault) {
457  int nargNonDefault = 0;
458  std::string nonDefName = answ;
459  // "superlong" because tLong might turn fName into an even longer name
460  std::string nameSuperLong = fName;
461  if (gInterpreterHelper)
462  gInterpreterHelper->GetPartiallyDesugaredName(nameSuperLong);
463  while (++nargNonDefault < narg) {
464  // If T<a> is a "typedef" (aka default template params)
465  // to T<a,b> then we can strip the "b".
466  const char* closeTemplate = " >";
467  if (nonDefName[nonDefName.length() - 1] != '>')
468  ++closeTemplate;
469  string nondef = nonDefName + closeTemplate;
470  if (gInterpreterHelper &&
471  gInterpreterHelper->IsAlreadyPartiallyDesugaredName(nondef, nameSuperLong))
472  break;
473  if (nargNonDefault>1) nonDefName += ",";
474  nonDefName += fElements[nargNonDefault];
475  }
476  if (nargNonDefault < narg)
477  narg = nargNonDefault;
478  }
479 #endif
480 
481  { for (int i=1;i<narg-1; i++) { answ += fElements[i]; answ+=",";} }
482  if (narg>1) { answ += fElements[narg-1]; }
483 
484  if (!fElements[0].empty()) {
485  if ( answ.at(answ.size()-1) == '>') {
486  answ += " >";
487  } else {
488  answ += '>';
489  }
490  }
491  if (fNestedLocation) {
492  // Treat X pf A<B>::X
493  fElements[fNestedLocation] = TClassEdit::ShortType(fElements[fNestedLocation].c_str(),mode);
494  answ += fElements[fNestedLocation];
495  }
496  // tail is not a type name, just [2], &, * etc.
497  if (tailLoc) answ += fElements[tailLoc].c_str()+tailOffset;
498 }
499 
500 ////////////////////////////////////////////////////////////////////////////////
501 /// Check if the type is a template
503 {
504  return !fElements[0].empty();
505 }
506 
507 ////////////////////////////////////////////////////////////////////////////////
508 /// Converts STL container name to number. vector -> 1, etc..
509 /// If len is greater than 0, only look at that many characters in the string.
510 
512 {
513  if (type.length() == 0)
514  return ROOT::kNotSTL;
515  size_t offset = 0;
516  if (type.compare(0,6,"const ")==0) { offset += 6; }
517  offset += StdLen(type.substr(offset));
518  const auto len = type.length() - offset;
519  if (len == 0)
520  return ROOT::kNotSTL;
521 
522  //container names
523  static const char *stls[] =
524  { "any", "vector", "list", "deque", "map", "multimap", "set", "multiset", "bitset",
525  "forward_list", "unordered_set", "unordered_multiset", "unordered_map", "unordered_multimap", 0};
526  static const size_t stllen[] =
527  { 3, 6, 4, 5, 3, 8, 3, 8, 6,
528  12, 13, 18, 13, 18, 0};
529  static const ROOT::ESTLType values[] =
535  // New C++11
540  };
541 
542  // kind of stl container
543  // find the correct ESTLType, skipping std::any (because I/O for it is not implemented yet?)
544  for (int k = 1; stls[k]; ++k) {
545  if (len == stllen[k]) {
546  if (type.compare(offset, len, stls[k]) == 0)
547  return values[k];
548  }
549  }
550  return ROOT::kNotSTL;
551 }
552 
553 ////////////////////////////////////////////////////////////////////////////////
554 /// Return number of arguments for STL container before allocator
555 
556 int TClassEdit::STLArgs(int kind)
557 {
558  static const char stln[] =// min number of container arguments
559  // vector, list, deque, map, multimap, set, multiset, bitset,
560  { 1, 1, 1, 1, 3, 3, 2, 2, 1,
561  // forward_list, unordered_set, unordered_multiset, unordered_map, unordered_multimap
562  1, 3, 3, 4, 4};
563 
564  return stln[kind];
565 }
566 
567 ////////////////////////////////////////////////////////////////////////////////
568 
569 static size_t findNameEnd(const std::string_view full)
570 {
571  int level = 0;
572  for(size_t i = 0; i < full.length(); ++i) {
573  switch(full[i]) {
574  case '<': { ++level; break; }
575  case '>': {
576  if (level == 0) return i;
577  else --level;
578  break;
579  }
580  case ',': {
581  if (level == 0) return i;
582  break;
583  }
584  default: break;
585  }
586  }
587  return full.length();
588 }
589 
590 ////////////////////////////////////////////////////////////////////////////////
591 
592 static size_t findNameEnd(const std::string &full, size_t pos)
593 {
594  return pos + findNameEnd( {full.data()+pos,full.length()-pos} );
595 }
596 
597 ////////////////////////////////////////////////////////////////////////////////
598 /// return whether or not 'allocname' is the STL default allocator for type
599 /// 'classname'
600 
601 bool TClassEdit::IsDefAlloc(const char *allocname, const char *classname)
602 {
603  string_view a( allocname );
604  RemoveStd(a);
605 
606  if (a=="alloc") return true;
607  if (a=="__default_alloc_template<true,0>") return true;
608  if (a=="__malloc_alloc_template<0>") return true;
609 
610  const static int alloclen = strlen("allocator<");
611  if (a.compare(0,alloclen,"allocator<") != 0) {
612  return false;
613  }
614  a.remove_prefix(alloclen);
615 
616  RemoveStd(a);
617 
618  string_view k = classname;
619  RemoveStd(k);
620 
621  if (a.compare(0,k.length(),k) != 0) {
622  // Now we need to compare the normalized name.
623  size_t end = findNameEnd(a);
624 
625  std::string valuepart;
626  GetNormalizedName(valuepart,std::string_view(a.data(),end));
627 
628  std::string norm_value;
629  GetNormalizedName(norm_value,k);
630 
631  if (valuepart != norm_value) {
632  return false;
633  }
634  a.remove_prefix(end);
635  } else {
636  a.remove_prefix(k.length());
637  }
638 
639  if (a.compare(0,1,">")!=0 && a.compare(0,2," >")!=0) {
640  return false;
641  }
642 
643  return true;
644 }
645 
646 ////////////////////////////////////////////////////////////////////////////////
647 /// return whether or not 'allocname' is the STL default allocator for a key
648 /// of type 'keyclassname' and a value of type 'valueclassname'
649 
650 bool TClassEdit::IsDefAlloc(const char *allocname,
651  const char *keyclassname,
652  const char *valueclassname)
653 {
654  if (IsDefAlloc(allocname,keyclassname)) return true;
655 
656  string_view a( allocname );
657  RemoveStd(a);
658 
659  const static int alloclen = strlen("allocator<");
660  if (a.compare(0,alloclen,"allocator<") != 0) {
661  return false;
662  }
663  a.remove_prefix(alloclen);
664 
665  RemoveStd(a);
666 
667  const static int pairlen = strlen("pair<");
668  if (a.compare(0,pairlen,"pair<") != 0) {
669  return false;
670  }
671  a.remove_prefix(pairlen);
672 
673  const static int constlen = strlen("const");
674  if (a.compare(0,constlen+1,"const ") == 0) {
675  a.remove_prefix(constlen+1);
676  }
677 
678  RemoveStd(a);
679 
680  string_view k = keyclassname;
681  RemoveStd(k);
682  if (k.compare(0,constlen+1,"const ") == 0) {
683  k.remove_prefix(constlen+1);
684  }
685 
686  if (a.compare(0,k.length(),k) != 0) {
687  // Now we need to compare the normalized name.
688  size_t end = findNameEnd(a);
689 
690  std::string alloc_keypart;
691  GetNormalizedName(alloc_keypart,std::string_view(a.data(),end));
692 
693  std::string norm_key;
694  GetNormalizedName(norm_key,k);
695 
696  if (alloc_keypart != norm_key) {
697  if ( norm_key[norm_key.length()-1] == '*' ) {
698  // also check with a trailing 'const'.
699  norm_key += "const";
700  } else {
701  norm_key += " const";
702  }
703  if (alloc_keypart != norm_key) {
704  return false;
705  }
706  }
707  a.remove_prefix(end);
708  } else {
709  size_t end = k.length();
710  if ( (a[end-1] == '*') || a[end]==' ' ) {
711  size_t skipSpace = (a[end] == ' ');
712  if (a.compare(end+skipSpace,constlen,"const") == 0) {
713  end += constlen+skipSpace;
714  }
715  }
716  a.remove_prefix(end);
717  }
718 
719  if (a[0] != ',') {
720  return false;
721  }
722  a.remove_prefix(1);
723  RemoveStd(a);
724 
725  string_view v = valueclassname;
726  RemoveStd(v);
727 
728  if (a.compare(0,v.length(),v) != 0) {
729  // Now we need to compare the normalized name.
730  size_t end = findNameEnd(a);
731 
732  std::string valuepart;
733  GetNormalizedName(valuepart,std::string_view(a.data(),end));
734 
735  std::string norm_value;
736  GetNormalizedName(norm_value,v);
737 
738  if (valuepart != norm_value) {
739  return false;
740  }
741  a.remove_prefix(end);
742  } else {
743  a.remove_prefix(v.length());
744  }
745 
746  if (a.compare(0,1,">")!=0 && a.compare(0,2," >")!=0) {
747  return false;
748  }
749 
750  return true;
751 }
752 
753 ////////////////////////////////////////////////////////////////////////////////
754 /// return whether or not 'elementName' is the STL default Element for type
755 /// 'classname'
756 
757 static bool IsDefElement(const char *elementName, const char* defaultElementName, const char *classname)
758 {
759  string c = elementName;
760 
761  size_t pos = StdLen(c);
762 
763  const int elementlen = strlen(defaultElementName);
764  if (c.compare(pos,elementlen,defaultElementName) != 0) {
765  return false;
766  }
767  pos += elementlen;
768 
769  string k = classname;
770  if (c.compare(pos,k.length(),k) != 0) {
771  // Now we need to compare the normalized name.
772  size_t end = findNameEnd(c,pos);
773 
774  std::string keypart;
775  if (pos != end) { // i.e. elementName != "std::less<>", see ROOT-11000.
776  TClassEdit::GetNormalizedName(keypart,std::string_view(c.c_str()+pos,end-pos));
777  }
778 
779  std::string norm_key;
780  TClassEdit::GetNormalizedName(norm_key,k);
781 
782  if (keypart != norm_key) {
783  return false;
784  }
785  pos = end;
786  } else {
787  pos += k.length();
788  }
789 
790  if (c.compare(pos,1,">")!=0 && c.compare(pos,2," >")!=0) {
791  return false;
792  }
793 
794  return true;
795 }
796 
797 ////////////////////////////////////////////////////////////////////////////////
798 /// return whether or not 'compare' is the STL default comparator for type
799 /// 'classname'
800 
801 bool TClassEdit::IsDefComp(const char *compname, const char *classname)
802 {
803  return IsDefElement(compname, "less<", classname);
804 }
805 
806 ////////////////////////////////////////////////////////////////////////////////
807 /// return whether or not 'predname' is the STL default predicate for type
808 /// 'classname'
809 
810 bool TClassEdit::IsDefPred(const char *predname, const char *classname)
811 {
812  return IsDefElement(predname, "equal_to<", classname);
813 }
814 
815 ////////////////////////////////////////////////////////////////////////////////
816 /// return whether or not 'hashname' is the STL default hash for type
817 /// 'classname'
818 
819 bool TClassEdit::IsDefHash(const char *hashname, const char *classname)
820 {
821  return IsDefElement(hashname, "hash<", classname);
822 }
823 
824 ////////////////////////////////////////////////////////////////////////////////
825 /// Return the normalized name. See TMetaUtils::GetNormalizedName.
826 ///
827 /// Return the type name normalized for ROOT,
828 /// keeping only the ROOT opaque typedef (Double32_t, etc.) and
829 /// removing the STL collections default parameter if any.
830 ///
831 /// Compare to TMetaUtils::GetNormalizedName, this routines does not
832 /// and can not add default template parameters.
833 
835 {
836  if (name.empty()) {
837  norm_name.clear();
838  return;
839  }
840 
841  norm_name = std::string(name); // NOTE: Is that the shortest version?
842 
844  // If there is a @ symbol (followed by a version number) then this is a synthetic class name created
845  // from an already normalized name for the purpose of supporting schema evolution.
846  return;
847  }
848 
849  // Remove the std:: and default template argument and insert the Long64_t and change basic_string to string.
852 
853  // 4 elements expected: "pair", "first type name", "second type name", "trailing stars"
854  if (splitname.fElements.size() == 4 && (splitname.fElements[0] == "std::pair" || splitname.fElements[0] == "pair" || splitname.fElements[0] == "__pair_base")) {
855  // We don't want to lookup the std::pair itself.
856  std::string first, second;
857  GetNormalizedName(first, splitname.fElements[1]);
858  GetNormalizedName(second, splitname.fElements[2]);
859  norm_name = splitname.fElements[0] + "<" + first + "," + second;
860  if (!second.empty() && second.back() == '>')
861  norm_name += " >";
862  else
863  norm_name += ">";
864  return;
865  }
866 
867  // Depending on how the user typed their code, in particular typedef
868  // declarations, we may end up with an explicit '::' being
869  // part of the result string. For consistency, we must remove it.
870  if (norm_name.length()>2 && norm_name[0]==':' && norm_name[1]==':') {
871  norm_name.erase(0,2);
872  }
873 
874  if (gInterpreterHelper) {
875  // See if the expanded name itself is a typedef.
876  std::string typeresult;
877  if (gInterpreterHelper->ExistingTypeCheck(norm_name, typeresult)
878  || gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(norm_name, typeresult)) {
879 
880  if (!typeresult.empty()) norm_name = typeresult;
881  }
882  }
883 }
884 
885 ////////////////////////////////////////////////////////////////////////////////
886 /// Replace 'long long' and 'unsigned long long' by 'Long64_t' and 'ULong64_t'
887 
888 string TClassEdit::GetLong64_Name(const char* original)
889 {
890  if (original==0)
891  return "";
892  else
893  return GetLong64_Name(string(original));
894 }
895 
896 ////////////////////////////////////////////////////////////////////////////////
897 /// Replace 'long long' and 'unsigned long long' by 'Long64_t' and 'ULong64_t'
898 
899 string TClassEdit::GetLong64_Name(const string& original)
900 {
901  static const char* longlong_s = "long long";
902  static const char* ulonglong_s = "unsigned long long";
903  static const unsigned int longlong_len = strlen(longlong_s);
904  static const unsigned int ulonglong_len = strlen(ulonglong_s);
905 
906  string result = original;
907 
908  int pos = 0;
909  while( (pos = result.find(ulonglong_s,pos) ) >=0 ) {
910  result.replace(pos, ulonglong_len, "ULong64_t");
911  }
912  pos = 0;
913  while( (pos = result.find(longlong_s,pos) ) >=0 ) {
914  result.replace(pos, longlong_len, "Long64_t");
915  }
916  return result;
917 }
918 
919 ////////////////////////////////////////////////////////////////////////////////
920 /// Return the start of the unqualified name include in 'original'.
921 
922 const char *TClassEdit::GetUnqualifiedName(const char *original)
923 {
924  const char *lastPos = original;
925  {
926  long depth = 0;
927  for(auto cursor = original; *cursor != '\0'; ++cursor) {
928  if ( *cursor == '<' || *cursor == '(') ++depth;
929  else if ( *cursor == '>' || *cursor == ')' ) --depth;
930  else if ( *cursor == ':' ) {
931  if (depth==0 && *(cursor+1) == ':' && *(cursor+2) != '\0') {
932  lastPos = cursor+2;
933  }
934  }
935  }
936  }
937  return lastPos;
938 }
939 
940 ////////////////////////////////////////////////////////////////////////////////
941 
942 static void R__FindTrailing(std::string &full, /*modified*/
943  std::string &stars /* the literal output */
944  )
945 {
946  const char *t = full.c_str();
947  const unsigned int tlen( full.size() );
948 
949  const char *starloc = t + tlen - 1;
950  bool hasconst = false;
951  if ( (*starloc)=='t'
952  && (starloc-t) > 4 && 0 == strncmp((starloc-4),"const",5)
953  && ( (*(starloc-5)) == ' ' || (*(starloc-5)) == '*' || (*(starloc-5)) == '&'
954  || (*(starloc-5)) == '>' || (*(starloc-5)) == ']') ) {
955  // we are ending on a const.
956  starloc -= 4;
957  if ((*starloc-1)==' ') {
958  // Take the space too.
959  starloc--;
960  }
961  hasconst = true;
962  }
963  if ( hasconst || (*starloc)=='*' || (*starloc)=='&' || (*starloc)==']' ) {
964  bool isArray = ( (*starloc)==']' );
965  while( t<=(starloc-1) && ((*(starloc-1))=='*' || (*(starloc-1))=='&' || (*(starloc-1))=='t' || isArray)) {
966  if (isArray) {
967  starloc--;
968  isArray = ! ( (*starloc)=='[' );
969  } else if ( (*(starloc-1))=='t' ) {
970  if ( (starloc-1-t) > 5 && 0 == strncmp((starloc-5),"const",5)
971  && ( (*(starloc-6)) == ' ' || (*(starloc-6)) == '*' || (*(starloc-6)) == '&'
972  || (*(starloc-6)) == '>' || (*(starloc-6)) == ']')) {
973  // we have a const.
974  starloc -= 5;
975  } else {
976  break;
977  }
978  } else {
979  starloc--;
980  }
981  }
982  stars = starloc;
983  if ((*(starloc-1))==' ') {
984  // erase the space too.
985  starloc--;
986  }
987 
988  const unsigned int starlen = strlen(starloc);
989  full.erase(tlen-starlen,starlen);
990  } else if (hasconst) {
991  stars = starloc;
992  const unsigned int starlen = strlen(starloc);
993  full.erase(tlen-starlen,starlen);
994  }
995 
996 }
997 
998 ////////////////////////////////////////////////////////////////////////////////
999 ////////////////////////////////////////////////////////////////////////////
1000 /// Stores in output (after emptying it) the split type.
1001 /// Stores the location of the tail (nested names) in nestedLoc (0 indicates no tail).
1002 /// Return the number of elements stored.
1003 ///
1004 /// First in list is the template name or is empty
1005 /// "vector<list<int>,alloc>**" to "vector" "list<int>" "alloc" "**"
1006 /// or "TNamed*" to "" "TNamed" "*"
1007 ////////////////////////////////////////////////////////////////////////////
1008 
1009 int TClassEdit::GetSplit(const char *type, vector<string>& output, int &nestedLoc, EModType mode)
1010 {
1011  nestedLoc = 0;
1012  output.clear();
1013  if (strlen(type)==0) return 0;
1014 
1015  int cleantypeMode = 1 /* keepInnerConst */;
1016  if (mode & kKeepOuterConst) {
1017  cleantypeMode = 0; /* remove only the outer class keyword */
1018  }
1019  string full( mode & kLong64 ? TClassEdit::GetLong64_Name( CleanType(type, cleantypeMode) )
1020  : CleanType(type, cleantypeMode) );
1021 
1022  // We need to replace basic_string with string.
1023  {
1024  unsigned int const_offset = (0==strncmp("const ",full.c_str(),6)) ? 6 : 0;
1025  bool isString = false;
1026  bool isStdString = false;
1027  size_t std_offset = const_offset;
1028  static const char* basic_string_std = "std::basic_string<char";
1029  static const unsigned int basic_string_std_len = strlen(basic_string_std);
1030 
1031  if (full.compare(const_offset,basic_string_std_len,basic_string_std) == 0
1032  && full.size() > basic_string_std_len) {
1033  isString = true;
1034  isStdString = true;
1035  std_offset += 5;
1036  } else if (full.compare(const_offset,basic_string_std_len-5,basic_string_std+5) == 0
1037  && full.size() > (basic_string_std_len-5)) {
1038  // no std.
1039  isString = true;
1040  } else if (full.find("basic_string") != std::string::npos) {
1041  size_t len = StdLen(full.c_str() + const_offset);
1042  if (len && len != 5 && full.compare(const_offset + len, basic_string_std_len-5, basic_string_std+5) == 0) {
1043  isString = true;
1044  isStdString = true;
1045  std_offset += len;
1046  }
1047  }
1048  if (isString) {
1049  size_t offset = basic_string_std_len - 5;
1050  offset += std_offset; // std_offset includs both the size of std prefix and const prefix.
1051  if ( full[offset] == '>' ) {
1052  // done.
1053  } else if (full[offset] == ',') {
1054  ++offset;
1055  if (full.compare(offset, 5, "std::") == 0) {
1056  offset += 5;
1057  }
1058  static const char* char_traits_s = "char_traits<char>";
1059  static const unsigned int char_traits_len = strlen(char_traits_s);
1060  if (full.compare(offset, char_traits_len, char_traits_s) == 0) {
1061  offset += char_traits_len;
1062  if ( full[offset] == '>') {
1063  // done.
1064  } else if (full[offset] == ' ' && full[offset+1] == '>') {
1065  ++offset;
1066  // done.
1067  } else if (full[offset] == ',') {
1068  ++offset;
1069  if (full.compare(offset, 5, "std::") == 0) {
1070  offset += 5;
1071  }
1072  static const char* allocator_s = "allocator<char>";
1073  static const unsigned int allocator_len = strlen(allocator_s);
1074  if (full.compare(offset, allocator_len, allocator_s) == 0) {
1075  offset += allocator_len;
1076  if ( full[offset] == '>') {
1077  // done.
1078  } else if (full[offset] == ' ' && full[offset+1] == '>') {
1079  ++offset;
1080  // done.
1081  } else {
1082  // Not std::string
1083  isString = false;
1084  }
1085  }
1086  } else {
1087  // Not std::string
1088  isString = false;
1089  }
1090  } else {
1091  // Not std::string.
1092  isString = false;
1093  }
1094  } else {
1095  // Not std::string.
1096  isString = false;
1097  }
1098  if (isString) {
1099  output.push_back(string());
1100  if (const_offset && (mode & kKeepOuterConst)) {
1101  if (isStdString && !(mode & kDropStd)) {
1102  output.push_back("const std::string");
1103  } else {
1104  output.push_back("const string");
1105  }
1106  } else {
1107  if (isStdString && !(mode & kDropStd)) {
1108  output.push_back("std::string");
1109  } else {
1110  output.push_back("string");
1111  }
1112  }
1113  if (offset < full.length()) {
1114  // Copy the trailing text.
1115  // keep the '>' inside right for R__FindTrailing to work
1116  string right( full.substr(offset) );
1117  string stars;
1118  R__FindTrailing(right, stars);
1119  output.back().append(right.c_str()+1); // skip the '>'
1120  output.push_back(stars);
1121  } else {
1122  output.push_back("");
1123  }
1124  return output.size();
1125  }
1126  }
1127  }
1128 
1129  if ( mode & kDropStd) {
1130  unsigned int offset = (0==strncmp("const ",full.c_str(),6)) ? 6 : 0;
1131  RemoveStd( full, offset );
1132  }
1133 
1134  string stars;
1135  if ( !full.empty() ) {
1136  R__FindTrailing(full, stars);
1137  }
1138 
1139  const char *c = strchr(full.c_str(),'<');
1140  if (c) {
1141  //we have 'something<'
1142  output.push_back(string(full,0,c - full.c_str()));
1143 
1144  const char *cursor;
1145  int level = 0;
1146  int parenthesis = 0;
1147  for(cursor = c + 1; *cursor != '\0' && !(level==0 && *cursor == '>'); ++cursor) {
1148  if (*cursor == '(') {
1149  ++parenthesis;
1150  continue;
1151  } else if (*cursor == ')') {
1152  --parenthesis;
1153  continue;
1154  }
1155  if (parenthesis)
1156  continue;
1157  switch (*cursor) {
1158  case '<': ++level; break;
1159  case '>': --level; break;
1160  case ',':
1161  if (level == 0) {
1162  output.push_back(std::string(c+1,cursor));
1163  c = cursor;
1164  }
1165  break;
1166  }
1167  }
1168  if (*cursor=='>') {
1169  if (*(cursor-1) == ' ') {
1170  output.push_back(std::string(c+1,cursor-1));
1171  } else {
1172  output.push_back(std::string(c+1,cursor));
1173  }
1174  // See what's next!
1175  if (*(cursor+1)==':') {
1176  // we have a name specified inside the class/namespace
1177  // For now we keep it in one piece
1178  nestedLoc = output.size();
1179  output.push_back((cursor+1));
1180  }
1181  } else if (level >= 0) {
1182  // Unterminated template
1183  output.push_back(std::string(c+1,cursor));
1184  }
1185  } else {
1186  //empty
1187  output.push_back(string());
1188  output.push_back(full);
1189  }
1190 
1191  if (!output.empty()) output.push_back(stars);
1192  return output.size();
1193 }
1194 
1195 
1196 ////////////////////////////////////////////////////////////////////////////////
1197 ////////////////////////////////////////////////////////////////////////////
1198 /// Cleanup type description, redundant blanks removed
1199 /// and redundant tail ignored
1200 /// return *tail = pointer to last used character
1201 /// if (mode==0) keep keywords
1202 /// if (mode==1) remove keywords outside the template params
1203 /// if (mode>=2) remove the keywords everywhere.
1204 /// if (tail!=0) cut before the trailing *
1205 ///
1206 /// The keywords currently are: "const" , "volatile" removed
1207 ///
1208 ///
1209 /// CleanType(" A<B, C< D, E> > *,F,G>") returns "A<B,C<D,E> >*"
1210 ////////////////////////////////////////////////////////////////////////////
1211 
1212 string TClassEdit::CleanType(const char *typeDesc, int mode, const char **tail)
1213 {
1214  static const char* remove[] = {"class","const","volatile",0};
1215  static bool isinit = false;
1216  static std::vector<size_t> lengths;
1217  if (!isinit) {
1218  for (int k=0; remove[k]; ++k) {
1219  lengths.push_back(strlen(remove[k]));
1220  }
1221  isinit = true;
1222  }
1223 
1224  string result;
1225  result.reserve(strlen(typeDesc)*2);
1226  int lev=0,kbl=1;
1227  const char* c;
1228 
1229  for(c=typeDesc;*c;c++) {
1230  if (c[0]==' ') {
1231  if (kbl) continue;
1232  if (!isalnum(c[ 1]) && c[ 1] !='_') continue;
1233  }
1234  if (kbl && (mode>=2 || lev==0)) { //remove "const' etc...
1235  int done = 0;
1236  int n = (mode) ? 999 : 1;
1237 
1238  // loop on all the keywords we want to remove
1239  for (int k=0; k<n && remove[k]; k++) {
1240  int rlen = lengths[k];
1241 
1242  // Do we have a match
1243  if (strncmp(remove[k],c,rlen)) continue;
1244 
1245  // make sure that the 'keyword' is not part of a longer indentifier
1246  if (isalnum(c[rlen]) || c[rlen]=='_' || c[rlen]=='$') continue;
1247 
1248  c+=rlen-1; done = 1; break;
1249  }
1250  if (done) continue;
1251  }
1252 
1253  kbl = (!isalnum(c[ 0]) && c[ 0]!='_' && c[ 0]!='$' && c[0]!='[' && c[0]!=']' && c[0]!='-' && c[0]!='@');
1254  // '@' is special character used only the artifical class name used by ROOT to implement the
1255  // I/O customization rules that requires caching of the input data.
1256 
1257  if (*c == '<' || *c == '(') lev++;
1258  if (lev==0 && !isalnum(*c)) {
1259  if (!strchr("*&:._$ []-@",*c)) break;
1260  // '.' is used as a module/namespace separator by PyROOT, see
1261  // TPyClassGenerator::GetClass.
1262  }
1263  if (c[0]=='>' && result.size() && result[result.size()-1]=='>') result+=" ";
1264 
1265  result += c[0];
1266 
1267  if (*c == '>' || *c == ')') lev--;
1268  }
1269  if(tail) *tail=c;
1270  return result;
1271 }
1272 
1273 ////////////////////////////////////////////////////////////////////////////////
1274 //////////////////////////////////////////////////////////////////////////////
1275 /// Return the absolute type of typeDesc.
1276 /// E.g.: typeDesc = "class const volatile TNamed**", returns "TNamed**".
1277 /// if (mode&1) remove last "*"s returns "TNamed"
1278 /// if (mode&2) remove default allocators from STL containers
1279 /// if (mode&4) remove all allocators from STL containers
1280 /// if (mode&8) return inner class of stl container. list<innerClass>
1281 /// if (mode&16) return deapest class of stl container. vector<list<deapest>>
1282 /// if (mode&kDropAllDefault) remove default template arguments
1283 //////////////////////////////////////////////////////////////////////////////
1284 
1285 string TClassEdit::ShortType(const char *typeDesc, int mode)
1286 {
1287  string answer;
1288 
1289  // get list of all arguments
1290  if (typeDesc) {
1291  TSplitType arglist(typeDesc, (EModType) mode);
1292  arglist.ShortType(answer, mode);
1293  }
1294 
1295  return answer;
1296 }
1297 
1298 ////////////////////////////////////////////////////////////////////////////////
1299 /// Return true if the type is one the interpreter details which are
1300 /// only forward declared (ClassInfo_t etc..)
1301 
1303 {
1304  size_t len = strlen(type);
1305  if (len < 2 || strncmp(type+len-2,"_t",2) != 0) return false;
1306 
1307  unsigned char offset = 0;
1308  if (strncmp(type,"const ",6)==0) { offset += 6; }
1309  static const char *names[] = { "CallFunc_t","ClassInfo_t","BaseClassInfo_t",
1310  "DataMemberInfo_t","FuncTempInfo_t","MethodInfo_t","MethodArgInfo_t",
1311  "TypeInfo_t","TypedefInfo_t",0};
1312 
1313  for(int k=1;names[k];k++) {if (strcmp(type+offset,names[k])==0) return true;}
1314  return false;
1315 }
1316 
1317 ////////////////////////////////////////////////////////////////////////////////
1318 /// Return true is the name is std::bitset<number> or bitset<number>
1319 
1320 bool TClassEdit::IsSTLBitset(const char *classname)
1321 {
1322  size_t offset = StdLen(classname);
1323  if ( strncmp(classname+offset,"bitset<",strlen("bitset<"))==0) return true;
1324  return false;
1325 }
1326 
1327 ////////////////////////////////////////////////////////////////////////////////
1328 /// Return the type of STL collection, if any, that is the underlying type
1329 /// of the given type. Namely return the value of IsSTLCont after stripping
1330 /// pointer, reference and constness from the type.
1331 /// UnderlyingIsSTLCont("vector<int>*") == IsSTLCont("vector<int>")
1332 /// See TClassEdit::IsSTLCont
1333 ///
1334 /// type : type name: vector<list<classA,allocator>,allocator>*
1335 /// result: 0 : not stl container
1336 /// code of container 1=vector,2=list,3=deque,4=map
1337 /// 5=multimap,6=set,7=multiset
1338 
1340 {
1341  if (type.compare(0,6,"const ",6) == 0)
1342  type.remove_prefix(6);
1343 
1344  while(type[type.length()-1]=='*' ||
1345  type[type.length()-1]=='&' ||
1346  type[type.length()-1]==' ') {
1347  type.remove_suffix(1);
1348  }
1349  return IsSTLCont(type);
1350 }
1351 
1352 ////////////////////////////////////////////////////////////////////////////////
1353 /// type : type name: vector<list<classA,allocator>,allocator>
1354 /// result: 0 : not stl container
1355 /// code of container 1=vector,2=list,3=deque,4=map
1356 /// 5=multimap,6=set,7=multiset
1357 
1359 {
1360  auto pos = type.find('<');
1361  if (pos==std::string_view::npos) return ROOT::kNotSTL;
1362 
1363  auto c = pos+1;
1364  for (decltype(type.length()) level = 1; c < type.length(); ++c) {
1365  if (type[c] == '<') ++level;
1366  if (type[c] == '>') --level;
1367  if (level == 0) break;
1368  }
1369  if (c != (type.length()-1) ) {
1370  return ROOT::kNotSTL;
1371  }
1372 
1373  return STLKind(type.substr(0,pos));
1374 }
1375 
1376 ////////////////////////////////////////////////////////////////////////////////
1377 /// type : type name: vector<list<classA,allocator>,allocator>
1378 /// testAlloc: if true, we test allocator, if it is not default result is negative
1379 /// result: 0 : not stl container
1380 /// abs(result): code of container 1=vector,2=list,3=deque,4=map
1381 /// 5=multimap,6=set,7=multiset
1382 /// positive val: we have a vector or list with default allocator to any depth
1383 /// like vector<list<vector<int>>>
1384 /// negative val: STL container other than vector or list, or non default allocator
1385 /// For example: vector<deque<int>> has answer -1
1386 
1387 int TClassEdit::IsSTLCont(const char *type, int testAlloc)
1388 {
1389  if (strchr(type,'<')==0) return 0;
1390 
1391  TSplitType arglist( type );
1392  return arglist.IsSTLCont(testAlloc);
1393 }
1394 
1395 ////////////////////////////////////////////////////////////////////////////////
1396 /// return true if the class belongs to the std namespace
1397 
1398 bool TClassEdit::IsStdClass(const char *classname)
1399 {
1400  classname += StdLen( classname );
1401  if ( strcmp(classname,"string")==0 ) return true;
1402  if ( strncmp(classname,"bitset<",strlen("bitset<"))==0) return true;
1403  if ( IsStdPair(classname) ) return true;
1404  if ( strcmp(classname,"allocator")==0) return true;
1405  if ( strncmp(classname,"allocator<",strlen("allocator<"))==0) return true;
1406  if ( strncmp(classname,"greater<",strlen("greater<"))==0) return true;
1407  if ( strncmp(classname,"less<",strlen("less<"))==0) return true;
1408  if ( strncmp(classname,"equal_to<",strlen("equal_to<"))==0) return true;
1409  if ( strncmp(classname,"hash<",strlen("hash<"))==0) return true;
1410  if ( strncmp(classname,"auto_ptr<",strlen("auto_ptr<"))==0) return true;
1411 
1412  if ( strncmp(classname,"vector<",strlen("vector<"))==0) return true;
1413  if ( strncmp(classname,"list<",strlen("list<"))==0) return true;
1414  if ( strncmp(classname,"forward_list<",strlen("forward_list<"))==0) return true;
1415  if ( strncmp(classname,"deque<",strlen("deque<"))==0) return true;
1416  if ( strncmp(classname,"map<",strlen("map<"))==0) return true;
1417  if ( strncmp(classname,"multimap<",strlen("multimap<"))==0) return true;
1418  if ( strncmp(classname,"set<",strlen("set<"))==0) return true;
1419  if ( strncmp(classname,"multiset<",strlen("multiset<"))==0) return true;
1420  if ( strncmp(classname,"unordered_set<",strlen("unordered_set<"))==0) return true;
1421  if ( strncmp(classname,"unordered_multiset<",strlen("unordered_multiset<"))==0) return true;
1422  if ( strncmp(classname,"unordered_map<",strlen("unordered_map<"))==0) return true;
1423  if ( strncmp(classname,"unordered_multimap<",strlen("unordered_multimap<"))==0) return true;
1424  if ( strncmp(classname,"bitset<",strlen("bitset<"))==0) return true;
1425 
1426  return false;
1427 }
1428 
1429 
1430 ////////////////////////////////////////////////////////////////////////////////
1431 
1432 bool TClassEdit::IsVectorBool(const char *name) {
1433  TSplitType splitname( name );
1434 
1435  return ( TClassEdit::STLKind( splitname.fElements[0] ) == ROOT::kSTLvector)
1436  && ( splitname.fElements[1] == "bool" || splitname.fElements[1]=="Bool_t");
1437 }
1438 
1439 ////////////////////////////////////////////////////////////////////////////////
1440 
1441 static void ResolveTypedefProcessType(const char *tname,
1442  unsigned int /* len */,
1443  unsigned int cursor,
1444  bool constprefix,
1445  unsigned int start_of_type,
1446  unsigned int end_of_type,
1447  unsigned int mod_start_of_type,
1448  bool &modified,
1449  std::string &result)
1450 {
1451  std::string type(modified && (mod_start_of_type < result.length()) ?
1452  result.substr(mod_start_of_type, string::npos)
1453  : string(tname, start_of_type, end_of_type == 0 ? cursor - start_of_type : end_of_type - start_of_type)); // we need to try to avoid this copy
1454  string typeresult;
1455  if (gInterpreterHelper->ExistingTypeCheck(type, typeresult)
1456  || gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(type, typeresult, false)) {
1457  // it is a known type
1458  if (!typeresult.empty()) {
1459  // and it is a typedef, we need to replace it in the output.
1460  if (modified) {
1461  result.replace(mod_start_of_type, string::npos,
1462  typeresult);
1463  }
1464  else {
1465  modified = true;
1466  mod_start_of_type = start_of_type;
1467  result += string(tname,0,start_of_type);
1468  if (constprefix && typeresult.compare(0,6,"const ",6) == 0) {
1469  result += typeresult.substr(6,string::npos);
1470  } else {
1471  result += typeresult;
1472  }
1473  }
1474  } else if (modified) {
1475  result.replace(mod_start_of_type, string::npos,
1476  type);
1477  }
1478  if (modified) {
1479  if (end_of_type != 0 && end_of_type!=cursor) {
1480  result += std::string(tname,end_of_type,cursor-end_of_type);
1481  }
1482  }
1483  } else {
1484  // no change needed.
1485  if (modified) {
1486  // result += type;
1487  if (end_of_type != 0 && end_of_type!=cursor) {
1488  result += std::string(tname,end_of_type,cursor-end_of_type);
1489  }
1490  }
1491  }
1492 }
1493 
1494 ////////////////////////////////////////////////////////////////////////////////
1495 
1496 static void ResolveTypedefImpl(const char *tname,
1497  unsigned int len,
1498  unsigned int &cursor,
1499  bool &modified,
1500  std::string &result)
1501 {
1502  // Need to parse and deal with
1503  // A::B::C< D, E::F, G::H<I,J>::K::L >::M
1504  // where E might be replace by N<O,P>
1505  // and G::H<I,J>::K or G might be a typedef.
1506 
1507  bool constprefix = false;
1508 
1509  if (tname[cursor]==' ') {
1510  if (!modified) {
1511  modified = true;
1512  result += string(tname,0,cursor);
1513  }
1514  while (tname[cursor]==' ') ++cursor;
1515  }
1516 
1517  if (tname[cursor]=='c' && (cursor+6<len)) {
1518  if (strncmp(tname+cursor,"const ",6) == 0) {
1519  cursor += 6;
1520  if (modified) result += "const ";
1521  }
1522  constprefix = true;
1523 
1524  }
1525 
1526  if (len > 2 && strncmp(tname+cursor,"::",2) == 0) {
1527  cursor += 2;
1528  }
1529 
1530  unsigned int start_of_type = cursor;
1531  unsigned int end_of_type = 0;
1532  unsigned int mod_start_of_type = result.length();
1533  unsigned int prevScope = cursor;
1534  for ( ; cursor<len; ++cursor) {
1535  switch (tname[cursor]) {
1536  case ':': {
1537  if ((cursor+1)>=len || tname[cursor+1]!=':') {
1538  // we expected another ':', malformed, give up.
1539  if (modified) result += (tname+prevScope);
1540  return;
1541  }
1542  string scope;
1543  if (modified) {
1544  scope = result.substr(mod_start_of_type, string::npos);
1545  scope += std::string(tname+prevScope,cursor-prevScope);
1546  } else {
1547  scope = std::string(tname, start_of_type, cursor - start_of_type); // we need to try to avoid this copy
1548  }
1549  std::string scoperesult;
1550  bool isInlined = false;
1551  if (gInterpreterHelper->ExistingTypeCheck(scope, scoperesult)
1552  ||gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(scope, scoperesult)) {
1553  // it is a known type
1554  if (!scoperesult.empty()) {
1555  // and it is a typedef
1556  if (modified) {
1557  if (constprefix && scoperesult.compare(0,6,"const ",6) != 0) mod_start_of_type -= 6;
1558  result.replace(mod_start_of_type, string::npos,
1559  scoperesult);
1560  result += "::";
1561  } else {
1562  modified = true;
1563  mod_start_of_type = start_of_type;
1564  result += string(tname,0,start_of_type);
1565  //if (constprefix) result += "const ";
1566  result += scoperesult;
1567  result += "::";
1568  }
1569  } else if (modified) {
1570  result += std::string(tname+prevScope,cursor+2-prevScope);
1571  }
1572  } else if (!gInterpreterHelper->IsDeclaredScope(scope,isInlined)) {
1573  // the nesting namespace is not declared, just ignore it and move on
1574  if (modified) result += std::string(tname+prevScope,cursor+2-prevScope);
1575  } else if (isInlined) {
1576  // humm ... just skip it.
1577  if (!modified) {
1578  modified = true;
1579  mod_start_of_type = start_of_type;
1580  result += string(tname,0,start_of_type);
1581  //if (constprefix) result += "const ";
1582  result += string(tname,start_of_type,prevScope - start_of_type);
1583  }
1584  } else if (modified) {
1585  result += std::string(tname+prevScope,cursor+2-prevScope);
1586  }
1587  // Consume the 1st semi colon, the 2nd will be consume by the for loop.
1588  ++cursor;
1589  prevScope = cursor+1;
1590  break;
1591  }
1592  case '<': {
1593  // push information on stack
1594  if (modified) {
1595  result += std::string(tname+prevScope,cursor+1-prevScope);
1596  // above includes the '<' .... result += '<';
1597  }
1598  do {
1599  ++cursor;
1600  ResolveTypedefImpl(tname,len,cursor,modified,result);
1601  } while( cursor<len && tname[cursor] == ',' );
1602 
1603  while (cursor<len && tname[cursor+1]==' ') ++cursor;
1604 
1605  // Since we already checked the type, skip the next section
1606  // (respective the scope section and final type processing section)
1607  // as they would re-do the same job.
1608  if (cursor+2<len && tname[cursor+1]==':' && tname[cursor+2]==':') {
1609  if (modified) result += "::";
1610  cursor += 2;
1611  prevScope = cursor+1;
1612  }
1613  if ( (cursor+1)<len && tname[cursor+1] == ',') {
1614  ++cursor;
1615  if (modified) result += ',';
1616  return;
1617  }
1618  if ( (cursor+1)<len && tname[cursor+1] == '>') {
1619  ++cursor;
1620  if (modified) result += " >";
1621  return;
1622  }
1623  if ( (cursor+1) >= len) {
1624  return;
1625  }
1626  if (tname[cursor] != ' ') break;
1627  if (modified) prevScope = cursor+1;
1628  // If the 'current' character is a space we need to treat it,
1629  // since this the next case statement, we can just fall through,
1630  // otherwise we should need to do:
1631  // --cursor; break;
1632  }
1633  case ' ': {
1634  end_of_type = cursor;
1635  // let's see if we have 'long long' or 'unsigned int' or 'signed char' or what not.
1636  while ((cursor+1)<len && tname[cursor+1] == ' ') ++cursor;
1637 
1638  auto next = cursor+1;
1639  if (strncmp(tname+next,"const",5) == 0 && ((next+5)==len || tname[next+5] == ' ' || tname[next+5] == '*' || tname[next+5] == '&' || tname[next+5] == ',' || tname[next+5] == '>' || tname[next+5] == ']'))
1640  {
1641  // A first const after the type needs to be move in the front.
1642  if (!modified) {
1643  modified = true;
1644  result += string(tname,0,start_of_type);
1645  result += "const ";
1646  mod_start_of_type = start_of_type + 6;
1647  result += string(tname,start_of_type,end_of_type-start_of_type);
1648  } else if (mod_start_of_type < result.length()) {
1649  result.insert(mod_start_of_type,"const ");
1650  mod_start_of_type += 6;
1651  } else {
1652  result += "const ";
1653  mod_start_of_type += 6;
1654  result += string(tname,start_of_type,end_of_type-start_of_type);
1655  }
1656  cursor += 5;
1657  end_of_type = cursor+1;
1658  prevScope = end_of_type;
1659  if ((next+5)==len || tname[next+5] == ',' || tname[next+5] == '>' || tname[next+5] == '[') {
1660  break;
1661  }
1662  } else if (next!=len && tname[next] != '*' && tname[next] != '&') {
1663  // the type is not ended yet.
1664  end_of_type = 0;
1665  break;
1666  }
1667  ++cursor;
1668  // Intentional fall through;
1669  }
1670  case '*':
1671  case '&': {
1672  if (tname[cursor] != ' ') end_of_type = cursor;
1673  // check and skip const (followed by *,&, ,) ... what about followed by ':','['?
1674  auto next = cursor+1;
1675  if (strncmp(tname+next,"const",5) == 0) {
1676  if ((next+5)==len || tname[next+5] == ' ' || tname[next+5] == '*' || tname[next+5] == '&' || tname[next+5] == ',' || tname[next+5] == '>' || tname[next+5] == '[') {
1677  next += 5;
1678  }
1679  }
1680  while (next<len &&
1681  (tname[next] == ' ' || tname[next] == '*' || tname[next] == '&')) {
1682  ++next;
1683  // check and skip const (followed by *,&, ,) ... what about followed by ':','['?
1684  if (strncmp(tname+next,"const",5) == 0) {
1685  if ((next+5)==len || tname[next+5] == ' ' || tname[next+5] == '*' || tname[next+5] == '&' || tname[next+5] == ',' || tname[next+5] == '>' || tname[next+5] == '[') {
1686  next += 5;
1687  }
1688  }
1689  }
1690  cursor = next-1;
1691 // if (modified && mod_start_of_type < result.length()) {
1692 // result += string(tname,end_of_type,cursor-end_of_type);
1693 // }
1694  break;
1695  }
1696  case ',': {
1697  if (modified && prevScope) {
1698  result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1699  }
1700  ResolveTypedefProcessType(tname,len,cursor,constprefix,start_of_type,end_of_type,mod_start_of_type,
1701  modified, result);
1702  if (modified) result += ',';
1703  return;
1704  }
1705  case '>': {
1706  if (modified && prevScope) {
1707  result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1708  }
1709  ResolveTypedefProcessType(tname,len,cursor,constprefix,start_of_type,end_of_type,mod_start_of_type,
1710  modified, result);
1711  if (modified) result += '>';
1712  return;
1713  }
1714  default:
1715  end_of_type = 0;
1716  }
1717  }
1718 
1719  if (prevScope && modified) result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1720 
1721  ResolveTypedefProcessType(tname,len,cursor,constprefix,start_of_type,end_of_type,mod_start_of_type,
1722  modified, result);
1723 }
1724 
1725 
1726 ////////////////////////////////////////////////////////////////////////////////
1727 
1728 string TClassEdit::ResolveTypedef(const char *tname, bool /* resolveAll */)
1729 {
1730  // Return the name of type 'tname' with all its typedef components replaced
1731  // by the actual type its points to
1732  // For example for "typedef MyObj MyObjTypedef;"
1733  // vector<MyObjTypedef> return vector<MyObj>
1734  //
1735 
1736  if (tname == 0 || tname[0] == 0)
1737  return "";
1738  if (!gInterpreterHelper)
1739  return tname;
1740 
1741  std::string result;
1742 
1743  // Check if we already know it is a normalized typename or a registered
1744  // typedef (i.e. known to gROOT).
1745  if (gInterpreterHelper->ExistingTypeCheck(tname, result))
1746  {
1747  if (result.empty()) return tname;
1748  else return result;
1749  }
1750 
1751  unsigned int len = strlen(tname);
1752 
1753  unsigned int cursor = 0;
1754  bool modified = false;
1755  ResolveTypedefImpl(tname,len,cursor,modified,result);
1756 
1757  if (!modified) return tname;
1758  else return result;
1759 }
1760 
1761 
1762 ////////////////////////////////////////////////////////////////////////////////
1763 
1764 string TClassEdit::InsertStd(const char *tname)
1765 {
1766  // Return the name of type 'tname' with all STL classes prepended by "std::".
1767  // For example for "vector<set<auto_ptr<int*> > >" it returns
1768  // "std::vector<std::set<std::auto_ptr<int*> > >"
1769  //
1770 
1771  static const char* sSTLtypes[] = {
1772  "allocator",
1773  "auto_ptr",
1774  "bad_alloc",
1775  "bad_cast",
1776  "bad_exception",
1777  "bad_typeid",
1778  "basic_filebuf",
1779  "basic_fstream",
1780  "basic_ifstream",
1781  "basic_ios",
1782  "basic_iostream",
1783  "basic_istream",
1784  "basic_istringstream",
1785  "basic_ofstream",
1786  "basic_ostream",
1787  "basic_ostringstream",
1788  "basic_streambuf",
1789  "basic_string",
1790  "basic_stringbuf",
1791  "basic_stringstream",
1792  "binary_function",
1793  "binary_negate",
1794  "bitset",
1795  "char_traits",
1796  "codecvt_byname",
1797  "codecvt",
1798  "collate",
1799  "collate_byname",
1800  "compare",
1801  "complex",
1802  "ctype_byname",
1803  "ctype",
1804  "deque",
1805  "divides",
1806  "domain_error",
1807  "equal_to",
1808  "exception",
1809  "forward_list",
1810  "fpos",
1811  "greater_equal",
1812  "greater",
1813  "gslice_array",
1814  "gslice",
1815  "hash",
1816  "indirect_array",
1817  "invalid_argument",
1818  "ios_base",
1819  "istream_iterator",
1820  "istreambuf_iterator",
1821  "istrstream",
1822  "iterator_traits",
1823  "iterator",
1824  "length_error",
1825  "less_equal",
1826  "less",
1827  "list",
1828  "locale",
1829  "localedef utility",
1830  "locale utility",
1831  "logic_error",
1832  "logical_and",
1833  "logical_not",
1834  "logical_or",
1835  "map",
1836  "mask_array",
1837  "mem_fun",
1838  "mem_fun_ref",
1839  "messages",
1840  "messages_byname",
1841  "minus",
1842  "modulus",
1843  "money_get",
1844  "money_put",
1845  "moneypunct",
1846  "moneypunct_byname",
1847  "multimap",
1848  "multiplies",
1849  "multiset",
1850  "negate",
1851  "not_equal_to",
1852  "num_get",
1853  "num_put",
1854  "numeric_limits",
1855  "numpunct",
1856  "numpunct_byname",
1857  "ostream_iterator",
1858  "ostreambuf_iterator",
1859  "ostrstream",
1860  "out_of_range",
1861  "overflow_error",
1862  "pair",
1863  "plus",
1864  "pointer_to_binary_function",
1865  "pointer_to_unary_function",
1866  "priority_queue",
1867  "queue",
1868  "range_error",
1869  "raw_storage_iterator",
1870  "reverse_iterator",
1871  "runtime_error",
1872  "set",
1873  "slice_array",
1874  "slice",
1875  "stack",
1876  "string",
1877  "strstream",
1878  "strstreambuf",
1879  "time_get_byname",
1880  "time_get",
1881  "time_put_byname",
1882  "time_put",
1883  "unary_function",
1884  "unary_negate",
1885  "unique_pointer",
1886  "underflow_error",
1887  "unordered_map",
1888  "unordered_multimap",
1889  "unordered_multiset",
1890  "unordered_set",
1891  "valarray",
1892  "vector",
1893  "wstring"
1894  };
1895  static ShuttingDownSignaler<set<string>> sSetSTLtypes;
1896 
1897  if (tname==0 || tname[0]==0) return "";
1898 
1899  if (sSetSTLtypes.empty()) {
1900  // set up static set
1901  const size_t nSTLtypes = sizeof(sSTLtypes) / sizeof(const char*);
1902  for (size_t i = 0; i < nSTLtypes; ++i)
1903  sSetSTLtypes.insert(sSTLtypes[i]);
1904  }
1905 
1906  size_t b = 0;
1907  size_t len = strlen(tname);
1908  string ret;
1909  ret.reserve(len + 20); // expect up to 4 extra "std::" to insert
1910  string id;
1911  while (b < len) {
1912  // find beginning of next identifier
1913  bool precScope = false; // whether the identifier was preceded by "::"
1914  while (!(isalnum(tname[b]) || tname[b] == '_') && b < len) {
1915  precScope = (b < len - 2) && (tname[b] == ':') && (tname[b + 1] == ':');
1916  if (precScope) {
1917  ret += "::";
1918  b += 2;
1919  } else
1920  ret += tname[b++];
1921  }
1922 
1923  // now b is at the beginning of an identifier or len
1924  size_t e = b;
1925  // find end of identifier
1926  id.clear();
1927  while (e < len && (isalnum(tname[e]) || tname[e] == '_'))
1928  id += tname[e++];
1929  if (!id.empty()) {
1930  if (!precScope) {
1931  set<string>::const_iterator iSTLtype = sSetSTLtypes.find(id);
1932  if (iSTLtype != sSetSTLtypes.end())
1933  ret += "std::";
1934  }
1935 
1936  ret += id;
1937  b = e;
1938  }
1939  }
1940  return ret;
1941 }
1942 
1943 ////////////////////////////////////////////////////////////////////////////////
1944 /// An helper class to dismount the name and remount it changed whenever
1945 /// necessary
1946 
1947 class NameCleanerForIO {
1948  std::string fName;
1949  std::vector<std::unique_ptr<NameCleanerForIO>> fArgumentNodes = {};
1950  NameCleanerForIO* fMother;
1951  bool fHasChanged = false;
1952  bool AreAncestorsSTLContOrArray()
1953  {
1954  NameCleanerForIO* mother = fMother;
1955  if (!mother) return false;
1956  bool isSTLContOrArray = true;
1957  while (nullptr != mother){
1958  auto stlType = TClassEdit::IsSTLCont(mother->fName+"<>");
1959  isSTLContOrArray &= ROOT::kNotSTL != stlType || TClassEdit::IsStdArray(mother->fName+"<");
1960  mother = mother->fMother;
1961  }
1962 
1963  return isSTLContOrArray;
1964  }
1965 
1966 public:
1967  NameCleanerForIO(const std::string& clName = "",
1969  NameCleanerForIO* mother = nullptr):fMother(mother)
1970  {
1971  if (clName.back() != '>') {
1972  fName = clName;
1973  return;
1974  }
1975 
1976  std::vector<std::string> v;
1977  int dummy=0;
1978  TClassEdit::GetSplit(clName.c_str(), v, dummy, mode);
1979 
1980  // We could be in presence of templates such as A1<T1>::A2<T2>::A3<T3>
1981  auto argsEnd = v.end();
1982  auto argsBeginPlusOne = ++v.begin();
1983  auto argPos = std::find_if(argsBeginPlusOne, argsEnd,
1984  [](std::string& arg){return (!arg.empty() && arg.front() == ':');});
1985  if (argPos != argsEnd) {
1986  const int lenght = clName.size();
1987  int wedgeBalance = 0;
1988  int lastOpenWedge = 0;
1989  for (int i=lenght-1;i>-1;i--) {
1990  auto& c = clName.at(i);
1991  if (c == '<') {
1992  wedgeBalance++;
1993  lastOpenWedge = i;
1994  } else if (c == '>') {
1995  wedgeBalance--;
1996  } else if (c == ':' && 0 == wedgeBalance) {
1997  // This would be A1<T1>::A2<T2>
1998  auto nameToClean = clName.substr(0,i-1);
1999  NameCleanerForIO node(nameToClean, mode);
2000  auto cleanName = node.ToString();
2001  fHasChanged = node.HasChanged();
2002  // We got A1<T1>::A2<T2> cleaned
2003 
2004  // We build the changed A1<T1>::A2<T2>::A3
2005  cleanName += "::";
2006  // Now we get A3 and append it
2007  cleanName += clName.substr(i+1,lastOpenWedge-i-1);
2008 
2009  // We now get the args of what in our case is A1<T1>::A2<T2>::A3
2010  auto lastTemplate = &clName.data()[i+1];
2011 
2012  // We split it
2013  TClassEdit::GetSplit(lastTemplate, v, dummy, mode);
2014  // We now replace the name of the template
2015  v[0] = cleanName;
2016  break;
2017  }
2018  }
2019  }
2020 
2021  fName = v.front();
2022  unsigned int nargs = v.size() - 2;
2023  for (unsigned int i=0;i<nargs;++i) {
2024  fArgumentNodes.emplace_back(new NameCleanerForIO(v[i+1],mode,this));
2025  }
2026  }
2027 
2028  bool HasChanged() const {return fHasChanged;}
2029 
2030  std::string ToString()
2031  {
2032  std::string name(fName);
2033 
2034  if (fArgumentNodes.empty()) return name;
2035 
2036  // We have in hands a case like unique_ptr< ... >
2037  // Perhaps we could treat atomics as well like this?
2038  if (!fMother && TClassEdit::IsUniquePtr(fName+"<")) {
2039  name = fArgumentNodes.front()->ToString();
2040  // ROOT-9933: we remove const if present.
2041  TClassEdit::TSplitType tst(name.c_str());
2042  tst.ShortType(name, 1);
2043  fHasChanged = true;
2044  return name;
2045  }
2046 
2047  // Now we treat the case of the collections of unique ptrs
2048  auto stlContType = AreAncestorsSTLContOrArray();
2049  if (stlContType != ROOT::kNotSTL && TClassEdit::IsUniquePtr(fName+"<")) {
2050  name = fArgumentNodes.front()->ToString();
2051  name += "*";
2052  fHasChanged = true;
2053  return name;
2054  }
2055 
2056  name += "<";
2057  for (auto& node : fArgumentNodes) {
2058  name += node->ToString() + ",";
2059  fHasChanged |= node->HasChanged();
2060  }
2061  name.pop_back(); // Remove the last comma.
2062  name += name.back() == '>' ? " >" : ">"; // Respect name normalisation
2063  return name;
2064  }
2065 
2066  const std::string& GetName() {return fName;}
2067  const std::vector<std::unique_ptr<NameCleanerForIO>>* GetChildNodes() const {return &fArgumentNodes;}
2068 };
2069 
2070 ////////////////////////////////////////////////////////////////////////////////
2071 
2072 std::string TClassEdit::GetNameForIO(const std::string& templateInstanceName,
2073  TClassEdit::EModType mode,
2074  bool* hasChanged)
2075 {
2076  // Decompose template name into pieces and remount it applying the necessary
2077  // transformations necessary for the ROOT IO subsystem, namely:
2078  // - Transform std::unique_ptr<T> into T (for selections) (also nested)
2079  // - Transform std::unique_ptr<const T> into T (for selections) (also nested)
2080  // - Transform std::COLL<std::unique_ptr<T>> into std::COLL<T*> (also nested)
2081  // Name normalisation is respected (e.g. spaces).
2082  // The implementation uses an internal class defined in the cxx file.
2083  NameCleanerForIO node(templateInstanceName, mode);
2084  auto nameForIO = node.ToString();
2085  if (hasChanged) {
2086  *hasChanged = node.HasChanged();
2087  }
2088  return nameForIO;
2089 }
2090 
2091 ////////////////////////////////////////////////////////////////////////////////
2092 // We could introduce a tuple as return type, but we be consistent with the rest
2093 // of the code.
2094 bool TClassEdit::GetStdArrayProperties(const char* typeName,
2095  std::string& typeNameBuf,
2096  std::array<int, 5>& maxIndices,
2097  int& ndim)
2098 {
2099  if (!IsStdArray(typeName)) return false;
2100 
2101  // We have an array, it's worth continuing
2102  NameCleanerForIO node(typeName);
2103 
2104  // We now recurse updating the data according to what we find
2105  auto childNodes = node.GetChildNodes();
2106  for (ndim = 1;ndim <=5 ; ndim++) {
2107  maxIndices[ndim-1] = std::atoi(childNodes->back()->GetName().c_str());
2108  auto& frontNode = childNodes->front();
2109  typeNameBuf = frontNode->GetName();
2110  if (! IsStdArray(typeNameBuf+"<")) {
2111  typeNameBuf = frontNode->ToString();
2112  return true;
2113  }
2114  childNodes = frontNode->GetChildNodes();
2115  }
2116 
2117  return true;
2118 }
2119 
2120 
2121 ////////////////////////////////////////////////////////////////////////////////
2122 /// Demangle in a portable way the type id name.
2123 /// IMPORTANT: The caller is responsible for freeing the returned const char*
2124 
2125 char* TClassEdit::DemangleTypeIdName(const std::type_info& ti, int& errorCode)
2126 {
2127  const char* mangled_name = ti.name();
2128  return DemangleName(mangled_name, errorCode);
2129 }
2130 /*
2131 /// Result of splitting a function declaration into
2132 /// fReturnType fScopeName::fFunctionName<fFunctionTemplateArguments>(fFunctionParameters)
2133 struct FunctionSplitInfo {
2134  /// Return type of the function, might be empty if the function declaration string did not provide it.
2135  std::string fReturnType;
2136 
2137  /// Name of the scope qualification of the function, possibly empty
2138  std::string fScopeName;
2139 
2140  /// Name of the function
2141  std::string fFunctionName;
2142 
2143  /// Template arguments of the function template specialization, if any; will contain one element "" for
2144  /// `function<>()`
2145  std::vector<std::string> fFunctionTemplateArguments;
2146 
2147  /// Function parameters.
2148  std::vector<std::string> fFunctionParameters;
2149 };
2150 */
2151 
2152 namespace {
2153  /// Find the first occurrence of any of needle's characters in haystack that
2154  /// is not nested in a <>, () or [] pair.
2155  std::size_t FindNonNestedNeedles(std::string_view haystack, string_view needles)
2156  {
2157  std::stack<char> expected;
2158  for (std::size_t pos = 0, end = haystack.length(); pos < end; ++pos) {
2159  char c = haystack[pos];
2160  if (expected.empty()) {
2161  if (needles.find(c) != std::string_view::npos)
2162  return pos;
2163  } else {
2164  if (c == expected.top()) {
2165  expected.pop();
2166  continue;
2167  }
2168  }
2169  switch (c) {
2170  case '<': expected.emplace('>'); break;
2171  case '(': expected.emplace(')'); break;
2172  case '[': expected.emplace(']'); break;
2173  }
2174  }
2175  return std::string_view::npos;
2176  }
2177 
2178  /// Find the first occurrence of `::` that is not nested in a <>, () or [] pair.
2179  std::size_t FindNonNestedDoubleColons(std::string_view haystack)
2180  {
2181  std::size_t lenHaystack = haystack.length();
2182  std::size_t prevAfterColumn = 0;
2183  while (true) {
2184  std::size_t posColumn = FindNonNestedNeedles(haystack.substr(prevAfterColumn), ":");
2185  if (posColumn == std::string_view::npos)
2186  return std::string_view::npos;
2187  prevAfterColumn += posColumn;
2188  // prevAfterColumn must have "::", i.e. two characters:
2189  if (prevAfterColumn + 1 >= lenHaystack)
2190  return std::string_view::npos;
2191 
2192  ++prevAfterColumn; // done with first (or only) ':'
2193  if (haystack[prevAfterColumn] == ':')
2194  return prevAfterColumn - 1;
2195  ++prevAfterColumn; // That was not a ':'.
2196  }
2197 
2198  return std::string_view::npos;
2199  }
2200 
2201  std::string_view StripSurroundingSpace(std::string_view str)
2202  {
2203  while (!str.empty() && std::isspace(str[0]))
2204  str.remove_prefix(1);
2205  while (!str.empty() && std::isspace(str.back()))
2206  str.remove_suffix(1);
2207  return str;
2208  }
2209 
2210  std::string ToString(std::string_view sv)
2211  {
2212  // ROOT's string_view backport doesn't add the new std::string contructor and assignment;
2213  // convert to std::string instead and assign that.
2214  return std::string(sv.data(), sv.length());
2215  }
2216 } // unnamed namespace
2217 
2218 /// Split a function declaration into its different parts.
2220 {
2221  // General structure:
2222  // `...` last-space `...` (`...`)
2223  // The first `...` is the return type.
2224  // The second `...` is the (possibly scoped) function name.
2225  // The third `...` are the parameters.
2226  // The function name can be of the form `...`<`...`>
2227  std::size_t posArgs = FindNonNestedNeedles(decl, "(");
2228  std::string_view declNoArgs = decl.substr(0, posArgs);
2229 
2230  std::size_t prevAfterWhiteSpace = 0;
2231  static const char whitespace[] = " \t\n";
2232  while (declNoArgs.length() > prevAfterWhiteSpace) {
2233  std::size_t posWS = FindNonNestedNeedles(declNoArgs.substr(prevAfterWhiteSpace), whitespace);
2234  if (posWS == std::string_view::npos)
2235  break;
2236  prevAfterWhiteSpace += posWS + 1;
2237  while (declNoArgs.length() > prevAfterWhiteSpace
2238  && strchr(whitespace, declNoArgs[prevAfterWhiteSpace]))
2239  ++prevAfterWhiteSpace;
2240  }
2241 
2242  /// Include any '&*' in the return type:
2243  std::size_t endReturn = prevAfterWhiteSpace;
2244  while (declNoArgs.length() > endReturn
2245  && strchr("&* \t \n", declNoArgs[endReturn]))
2246  ++endReturn;
2247 
2248  result.fReturnType = ToString(StripSurroundingSpace(declNoArgs.substr(0, endReturn)));
2249 
2250  /// scope::anotherscope::functionName<tmplt>:
2251  std::string_view scopeFunctionTmplt = declNoArgs.substr(endReturn);
2252  std::size_t prevAtScope = FindNonNestedDoubleColons(scopeFunctionTmplt);
2253  while (prevAtScope != std::string_view::npos
2254  && scopeFunctionTmplt.length() > prevAtScope + 2) {
2255  std::size_t posScope = FindNonNestedDoubleColons(scopeFunctionTmplt.substr(prevAtScope + 2));
2256  if (posScope == std::string_view::npos)
2257  break;
2258  prevAtScope += posScope + 2;
2259  }
2260 
2261  std::size_t afterScope = prevAtScope + 2;
2262  if (prevAtScope == std::string_view::npos) {
2263  afterScope = 0;
2264  prevAtScope = 0;
2265  }
2266 
2267  result.fScopeName = ToString(StripSurroundingSpace(scopeFunctionTmplt.substr(0, prevAtScope)));
2268  std::string_view funcNameTmplArgs = scopeFunctionTmplt.substr(afterScope);
2269 
2270  result.fFunctionTemplateArguments.clear();
2271  std::size_t posTmpltOpen = FindNonNestedNeedles(funcNameTmplArgs, "<");
2272  if (posTmpltOpen != std::string_view::npos) {
2273  result.fFunctionName = ToString(StripSurroundingSpace(funcNameTmplArgs.substr(0, posTmpltOpen)));
2274 
2275  // Parse template parameters:
2276  std::string_view tmpltArgs = funcNameTmplArgs.substr(posTmpltOpen + 1);
2277  std::size_t posTmpltClose = FindNonNestedNeedles(tmpltArgs, ">");
2278  if (posTmpltClose != std::string_view::npos) {
2279  tmpltArgs = tmpltArgs.substr(0, posTmpltClose);
2280  std::size_t prevAfterArg = 0;
2281  while (tmpltArgs.length() > prevAfterArg) {
2282  std::size_t posComma = FindNonNestedNeedles(tmpltArgs.substr(prevAfterArg), ",");
2283  if (posComma == std::string_view::npos) {
2284  break;
2285  }
2286  result.fFunctionTemplateArguments.emplace_back(ToString(StripSurroundingSpace(tmpltArgs.substr(prevAfterArg, posComma))));
2287  prevAfterArg += posComma + 1;
2288  }
2289  // Add the trailing arg.
2290  result.fFunctionTemplateArguments.emplace_back(ToString(StripSurroundingSpace(tmpltArgs.substr(prevAfterArg))));
2291  }
2292  } else {
2293  result.fFunctionName = ToString(StripSurroundingSpace(funcNameTmplArgs));
2294  }
2295 
2296  result.fFunctionParameters.clear();
2297  if (posArgs != std::string_view::npos) {
2298  /// (params)
2299  std::string_view params = decl.substr(posArgs + 1);
2300  std::size_t posEndArgs = FindNonNestedNeedles(params, ")");
2301  if (posEndArgs != std::string_view::npos) {
2302  params = params.substr(0, posEndArgs);
2303  std::size_t prevAfterArg = 0;
2304  while (params.length() > prevAfterArg) {
2305  std::size_t posComma = FindNonNestedNeedles(params.substr(prevAfterArg), ",");
2306  if (posComma == std::string_view::npos) {
2307  result.fFunctionParameters.emplace_back(ToString(StripSurroundingSpace(params.substr(prevAfterArg))));
2308  break;
2309  }
2310  result.fFunctionParameters.emplace_back(ToString(StripSurroundingSpace(params.substr(prevAfterArg, posComma))));
2311  prevAfterArg += posComma + 1; // skip ','
2312  }
2313  }
2314  }
2315 
2316  return true;
2317 }
TClassEdit::FunctionSplitInfo
Result of splitting a function declaration into fReturnType fScopeName::fFunctionName<fFunctionTempla...
Definition: TClassEdit.h:249
c
#define c(i)
Definition: RSha256.hxx:101
n
const Int_t n
Definition: legend1.C:16
ROOT::kSTLdeque
@ kSTLdeque
Definition: ESTLType.h:32
first
Definition: first.py:1
ROOT::kSTLvector
@ kSTLvector
Definition: ESTLType.h:30
ROOT::kSTLset
@ kSTLset
Definition: ESTLType.h:35
Rstrstream.h
e
#define e(i)
Definition: RSha256.hxx:103
ROOT::kSTLunorderedmultimap
@ kSTLunorderedmultimap
Definition: ESTLType.h:45
ROOT::kSTLbitset
@ kSTLbitset
Definition: ESTLType.h:37
TClassEdit::GetNameForIO
std::string GetNameForIO(const std::string &templateInstanceName, TClassEdit::EModType mode=TClassEdit::kNone, bool *hasChanged=nullptr)
Definition: TClassEdit.cxx:2072
TClassEdit::GetNormalizedName
void GetNormalizedName(std::string &norm_name, std::string_view name)
Return the normalized name.
Definition: TClassEdit.cxx:834
TClassEdit::TInterpreterLookupHelper::GetPartiallyDesugaredNameWithScopeHandling
virtual bool GetPartiallyDesugaredNameWithScopeHandling(const std::string &, std::string &, bool=true)=0
TClassEdit::FunctionSplitInfo::fFunctionName
std::string fFunctionName
Name of the function.
Definition: TClassEdit.h:257
TClassEdit::GetStdArrayProperties
bool GetStdArrayProperties(const char *typeName, std::string &typeNameBuf, std::array< int, 5 > &maxIndices, int &ndim)
Definition: TClassEdit.cxx:2094
ROOT::kSTLunorderedmultiset
@ kSTLunorderedmultiset
Definition: ESTLType.h:43
ROOT::kNotSTL
@ kNotSTL
Definition: ESTLType.h:29
TClassEdit::TInterpreterLookupHelper::ExistingTypeCheck
virtual bool ExistingTypeCheck(const std::string &, std::string &)=0
TClassEdit::ShortType
std::string ShortType(const char *typeDesc, int mode)
Return the absolute type of typeDesc.
Definition: TClassEdit.cxx:1285
TClassEdit::STLArgs
int STLArgs(int kind)
Return number of arguments for STL container before allocator.
Definition: TClassEdit.cxx:556
TClassEdit::kLong64
@ kLong64
Definition: TClassEdit.h:84
output
static void output(int code)
Definition: gifencode.c:226
string_view
basic_string_view< char > string_view
Definition: libcpp_string_view.h:785
TClassEdit::FunctionSplitInfo::fScopeName
std::string fScopeName
Name of the scope qualification of the function, possibly empty.
Definition: TClassEdit.h:254
TClassEdit::kDropAlloc
@ kDropAlloc
Definition: TClassEdit.h:78
TClassEdit::IsStdClass
bool IsStdClass(const char *type)
return true if the class belongs to the std namespace
Definition: TClassEdit.cxx:1398
TClassEdit::GetLong64_Name
std::string GetLong64_Name(const char *original)
Replace 'long long' and 'unsigned long long' by 'Long64_t' and 'ULong64_t'.
Definition: TClassEdit.cxx:888
TClassEdit::IsVectorBool
bool IsVectorBool(const char *name)
Definition: TClassEdit.cxx:1432
TClassEdit::TSplitType::IsInSTL
ROOT::ESTLType IsInSTL() const
type : type name: vector<list<classA,allocator>,allocator>[::iterator] result: 0 : not stl container ...
Definition: TClassEdit.cxx:172
TClassEdit::TSplitType::IsSTLCont
int IsSTLCont(int testAlloc=0) const
type : type name: vector<list<classA,allocator>,allocator> testAlloc: if true, we test allocator,...
Definition: TClassEdit.cxx:189
TClassEdit::kDropDefaultAlloc
@ kDropDefaultAlloc
Definition: TClassEdit.h:77
TClassEdit::TSplitType
Definition: TClassEdit.h:137
TClassEdit::SplitFunction
bool SplitFunction(std::string_view decl, FunctionSplitInfo &result)
Split a function declaration into its different parts.
Definition: TClassEdit.cxx:2219
ROOT::kSTLlist
@ kSTLlist
Definition: ESTLType.h:31
TClassEdit::kDropAllDefault
@ kDropAllDefault
Definition: TClassEdit.h:83
v
@ v
Definition: rootcling_impl.cxx:3643
b
#define b(i)
Definition: RSha256.hxx:100
TClassEdit::kResolveTypedef
@ kResolveTypedef
Definition: TClassEdit.h:87
TClassEdit::TInterpreterLookupHelper::ShuttingDownSignal
virtual void ShuttingDownSignal()=0
IsDefElement
static bool IsDefElement(const char *elementName, const char *defaultElementName, const char *classname)
return whether or not 'elementName' is the STL default Element for type 'classname'
Definition: TClassEdit.cxx:757
ROOT::kSTLmap
@ kSTLmap
Definition: ESTLType.h:33
id
XFontStruct * id
Definition: TGX11.cxx:109
TClassEdit::TSplitType::fElements
std::vector< std::string > fElements
Definition: TClassEdit.h:140
ResolveTypedefImpl
static void ResolveTypedefImpl(const char *tname, unsigned int len, unsigned int &cursor, bool &modified, std::string &result)
Definition: TClassEdit.cxx:1496
TClassEdit::IsArtificial
bool IsArtificial(std::string_view name)
Definition: TClassEdit.h:158
ROOT::ESTLType
ESTLType
Definition: ESTLType.h:28
TClassEdit::FunctionSplitInfo::fReturnType
std::string fReturnType
Return type of the function, might be empty if the function declaration string did not provide it.
Definition: TClassEdit.h:251
kNone
const Handle_t kNone
Definition: GuiTypes.h:88
TClassEdit::DemangleName
char * DemangleName(const char *mangled_name, int &errorCode)
Definition: TClassEdit.h:217
TClassEdit::IsSTLCont
ROOT::ESTLType IsSTLCont(std::string_view type)
type : type name: vector<list<classA,allocator>,allocator> result: 0 : not stl container code of cont...
Definition: TClassEdit.cxx:1358
TClassEdit::kNone
@ kNone
Definition: TClassEdit.h:75
ROOT::kSTLmultiset
@ kSTLmultiset
Definition: ESTLType.h:36
TClassEdit::IsStdPair
bool IsStdPair(std::string_view name)
Definition: TClassEdit.h:190
ROOT::kSTLforwardlist
@ kSTLforwardlist
Definition: ESTLType.h:41
TClassEdit::EComplexType
EComplexType
Definition: TClassEdit.h:110
StdLen
static size_t StdLen(const std::string_view name)
Return the length, if any, taken by std:: and any potential inline namespace (well compiler detail na...
Definition: TClassEdit.cxx:51
TClassEdit::CleanType
std::string CleanType(const char *typeDesc, int mode=0, const char **tail=0)
Cleanup type description, redundant blanks removed and redundant tail ignored return *tail = pointer ...
Definition: TClassEdit.cxx:1212
TClassEdit::TInterpreterLookupHelper
Definition: TClassEdit.h:120
a
auto * a
Definition: textangle.C:12
RStringView.hxx
TClassEdit::IsSTLBitset
bool IsSTLBitset(const char *type)
Return true is the name is std::bitset<number> or bitset<number>
Definition: TClassEdit.cxx:1320
TClassEdit::kDropComparator
@ kDropComparator
Definition: TClassEdit.h:82
TClassEdit::TInterpreterLookupHelper::IsAlreadyPartiallyDesugaredName
virtual bool IsAlreadyPartiallyDesugaredName(const std::string &, const std::string &)=0
ROOT::Math::Util::ToString
std::string ToString(const T &val)
Utility function for conversion to strings.
Definition: Util.h:50
ROOT::kSTLunorderedset
@ kSTLunorderedset
Definition: ESTLType.h:42
TClassEdit::IsDefComp
bool IsDefComp(const char *comp, const char *classname)
return whether or not 'compare' is the STL default comparator for type 'classname'
Definition: TClassEdit.cxx:801
ROOT::kSTLmultimap
@ kSTLmultimap
Definition: ESTLType.h:34
TClassEdit::GetUnqualifiedName
const char * GetUnqualifiedName(const char *name)
Return the start of the unqualified name include in 'original'.
Definition: TClassEdit.cxx:922
TClassEdit::TSplitType::fNestedLocation
int fNestedLocation
Definition: TClassEdit.h:141
TClassEdit::TInterpreterLookupHelper::IsDeclaredScope
virtual bool IsDeclaredScope(const std::string &, bool &)=0
TClassEdit::IsInterpreterDetail
bool IsInterpreterDetail(const char *type)
Return true if the type is one the interpreter details which are only forward declared (ClassInfo_t e...
Definition: TClassEdit.cxx:1302
TClassEdit::IsDefHash
bool IsDefHash(const char *hashname, const char *classname)
return whether or not 'hashname' is the STL default hash for type 'classname'
Definition: TClassEdit.cxx:819
TClassEdit::IsDefPred
bool IsDefPred(const char *predname, const char *classname)
return whether or not 'predname' is the STL default predicate for type 'classname'
Definition: TClassEdit.cxx:810
TClassEdit::TSplitType::ShortType
void ShortType(std::string &answer, int mode)
Return the absolute type of typeDesc into the string answ.
Definition: TClassEdit.cxx:233
TClassEdit::InsertStd
std::string InsertStd(const char *tname)
Definition: TClassEdit.cxx:1764
RemoveStd
static void RemoveStd(std::string &name, size_t pos=0)
Remove std:: and any potential inline namespace (well compiler detail namespace.
Definition: TClassEdit.cxx:98
TClassEdit::GetSplit
int GetSplit(const char *type, std::vector< std::string > &output, int &nestedLoc, EModType mode=TClassEdit::kNone)
Stores in output (after emptying it) the split type.
Definition: TClassEdit.cxx:1009
TClassEdit::TSplitType::IsTemplate
bool IsTemplate()
Check if the type is a template.
Definition: TClassEdit.cxx:502
TClassEdit::IsUniquePtr
bool IsUniquePtr(std::string_view name)
Definition: TClassEdit.h:186
TClassEdit::TInterpreterLookupHelper::GetPartiallyDesugaredName
virtual void GetPartiallyDesugaredName(std::string &)=0
TClassEdit.h
TClassEdit::UnderlyingIsSTLCont
ROOT::ESTLType UnderlyingIsSTLCont(std::string_view type)
Return the type of STL collection, if any, that is the underlying type of the given type.
Definition: TClassEdit.cxx:1339
TClassEdit::TInterpreterLookupHelper::~TInterpreterLookupHelper
virtual ~TInterpreterLookupHelper()
Definition: TClassEdit.cxx:141
TClassEdit::EModType
EModType
Definition: TClassEdit.h:74
TClassEdit::Init
void Init(TClassEdit::TInterpreterLookupHelper *helper)
Definition: TClassEdit.cxx:154
name
char name[80]
Definition: TGX11.cxx:110
TClassEdit::kDropStd
@ kDropStd
Definition: TClassEdit.h:85
ROOT::kSTLunorderedmap
@ kSTLunorderedmap
Definition: ESTLType.h:44
ROOT::Math::Chebyshev::T
double T(double x)
Definition: ChebyshevPol.h:34
TClassEdit::ResolveTypedef
std::string ResolveTypedef(const char *tname, bool resolveAll=false)
Definition: TClassEdit.cxx:1728
TClassEdit::STLKind
ROOT::ESTLType STLKind(std::string_view type)
Converts STL container name to number.
Definition: TClassEdit.cxx:511
findNameEnd
static size_t findNameEnd(const std::string_view full)
Definition: TClassEdit.cxx:569
type
int type
Definition: TGX11.cxx:121
TClassEdit::FunctionSplitInfo::fFunctionParameters
std::vector< std::string > fFunctionParameters
Function parameters.
Definition: TClassEdit.h:264
TClassEdit::kKeepOuterConst
@ kKeepOuterConst
Definition: TClassEdit.h:86
ResolveTypedefProcessType
static void ResolveTypedefProcessType(const char *tname, unsigned int, unsigned int cursor, bool constprefix, unsigned int start_of_type, unsigned int end_of_type, unsigned int mod_start_of_type, bool &modified, std::string &result)
Definition: TClassEdit.cxx:1441
TClassEdit::FunctionSplitInfo::fFunctionTemplateArguments
std::vector< std::string > fFunctionTemplateArguments
Template arguments of the function template specialization, if any; will contain one element "" for f...
Definition: TClassEdit.h:261
TGeant4Unit::second
static constexpr double second
Definition: TGeant4SystemOfUnits.h:151
TClassEdit::GetComplexType
EComplexType GetComplexType(const char *)
Definition: TClassEdit.cxx:120
TClassEdit::kDropStlDefault
@ kDropStlDefault
Definition: TClassEdit.h:81
R__FindTrailing
static void R__FindTrailing(std::string &full, std::string &stars)
Definition: TClassEdit.cxx:942
TClassEdit::DemangleTypeIdName
char * DemangleTypeIdName(const std::type_info &ti, int &errorCode)
Demangle in a portable way the type id name.
Definition: TClassEdit.cxx:2125
TClassEdit::IsDefAlloc
bool IsDefAlloc(const char *alloc, const char *classname)
return whether or not 'allocname' is the STL default allocator for type 'classname'
Definition: TClassEdit.cxx:601
TClassEdit::TSplitType::TSplitType
TSplitType(const char *type2split, EModType mode=TClassEdit::kNone)
default constructor
Definition: TClassEdit.cxx:162
TClassEdit::IsStdArray
bool IsStdArray(std::string_view name)
Definition: TClassEdit.h:188