ROOT  6.06/09
Reference Guide
TClassEdit.cxx
Go to the documentation of this file.
1 // @(#)root/metautils:$Id$
2 // Author: Victor Perev 04/10/2003
3 // Philippe Canal 05/2004
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <assert.h>
8 #include <string.h>
9 #include "TClassEdit.h"
10 #include <ctype.h>
11 #include "Rstrstream.h"
12 #include <set>
13 // for shared_ptr
14 #include <memory>
15 #include "RStringView.h"
16 
17 namespace {
18  static TClassEdit::TInterpreterLookupHelper *gInterpreterHelper = 0;
19 }
20 
21 namespace std {} using namespace std;
22 
23 ////////////////////////////////////////////////////////////////////////////////
24 /// Return the length, if any, taken by std:: and any
25 /// potential inline namespace (well compiler detail namespace).
26 
27 static size_t StdLen(const std::string_view name)
28 {
29  size_t len = 0;
30  if (name.compare(0,5,"std::")==0) {
31  len = 5;
32 
33  // TODO: This is likely to induce unwanted autoparsing, those are reduced
34  // by the caching of the result.
35  if (gInterpreterHelper) {
36  for(size_t i = 5; i < name.length(); ++i) {
37  if (name[i] == '<') break;
38  if (name[i] == ':') {
39  bool isInlined;
40  std::string scope(name.data(),i);
41  std::string scoperesult;
42  // We assume that we are called in already serialized code.
43  // Note: should we also cache the negative answers?
44  static std::set<std::string> gInlined;
45 
46  if (gInlined.find(scope) != gInlined.end()) {
47  len = i;
48  if (i+1<name.length() && name[i+1]==':') {
49  len += 2;
50  }
51  }
52  if (!gInterpreterHelper->ExistingTypeCheck(scope, scoperesult)
53  && gInterpreterHelper->IsDeclaredScope(scope,isInlined)) {
54  if (isInlined) {
55  gInlined.insert(scope);
56  len = i;
57  if (i+1<name.length() && name[i+1]==':') {
58  len += 2;
59  }
60  }
61  }
62  }
63  }
64  }
65  }
66 
67  return len;
68 }
69 
70 ////////////////////////////////////////////////////////////////////////////////
71 /// Remove std:: and any potential inline namespace (well compiler detail
72 /// namespace.
73 
74 static void RemoveStd(std::string &name, size_t pos = 0)
75 {
76  size_t len = StdLen({name.data()+pos,name.length()-pos});
77  if (len) {
78  name.erase(pos,len);
79  }
80 }
81 
82 ////////////////////////////////////////////////////////////////////////////////
83 /// Remove std:: and any potential inline namespace (well compiler detail
84 /// namespace.
85 
86 static void RemoveStd(std::string_view &name)
87 {
88  size_t len = StdLen(name);
89  if (len) {
90  name.remove_prefix(len);
91  }
92 }
93 
94 ////////////////////////////////////////////////////////////////////////////////
95 
97 {
98  if (0 == strncmp(clName, "complex<", 8)) {
99  const char *clNamePlus8 = clName + 8;
100  if (0 == strcmp("float>", clNamePlus8)) {
101  return EComplexType::kFloat;
102  }
103  if (0 == strcmp("double>", clNamePlus8)) {
104  return EComplexType::kDouble;
105  }
106  if (0 == strcmp("int>", clNamePlus8)) {
107  return EComplexType::kInt;
108  }
109  if (0 == strcmp("long>", clNamePlus8)) {
110  return EComplexType::kLong;
111  }
112  }
113  return EComplexType::kNone;
114 }
115 
116 ////////////////////////////////////////////////////////////////////////////////
117 
119 {
120  gInterpreterHelper = helper;
121 }
122 
123 ////////////////////////////////////////////////////////////////////////////////
124 /// default constructor
125 
126 TClassEdit::TSplitType::TSplitType(const char *type2split, EModType mode) : fName(type2split), fNestedLocation(0)
127 {
128  TClassEdit::GetSplit(type2split, fElements, fNestedLocation, mode);
129 }
130 
131 ////////////////////////////////////////////////////////////////////////////////
132 /// type : type name: vector<list<classA,allocator>,allocator>[::iterator]
133 /// result: 0 : not stl container and not declared inside an stl container.
134 /// result: code of container that the type or is the scope of the type
135 
137 {
138  if (fElements[0].empty()) return ROOT::kNotSTL;
139  return STLKind(fElements[0]);
140 }
141 
142 ////////////////////////////////////////////////////////////////////////////////
143 /// type : type name: vector<list<classA,allocator>,allocator>
144 /// testAlloc: if true, we test allocator, if it is not default result is negative
145 /// result: 0 : not stl container
146 /// abs(result): code of container 1=vector,2=list,3=deque,4=map
147 /// 5=multimap,6=set,7=multiset
148 /// positive val: we have a vector or list with default allocator to any depth
149 /// like vector<list<vector<int>>>
150 /// negative val: STL container other than vector or list, or non default allocator
151 /// For example: vector<deque<int>> has answer -1
152 
153 int TClassEdit::TSplitType::IsSTLCont(int testAlloc) const
154 {
155 
156  if (fElements[0].empty()) return 0;
157  int numb = fElements.size();
158  if (!fElements[numb-1].empty() && fElements[numb-1][0]=='*') --numb;
159 
160  if ( fNestedLocation ) {
161  // The type has been defined inside another namespace and/or class
162  // this couldn't possibly be an STL container
163  return 0;
164  }
165 
166  int kind = STLKind(fElements[0]);
167 
168  if (kind==ROOT::kSTLvector || kind==ROOT::kSTLlist || kind==ROOT::kSTLforwardlist) {
169 
170  int nargs = STLArgs(kind);
171  if (testAlloc && (numb-1 > nargs) && !IsDefAlloc(fElements[numb-1].c_str(),fElements[1].c_str())) {
172 
173  // We have a non default allocator,
174  // let's return a negative value.
175 
176  kind = -kind;
177 
178  } else {
179 
180  // We has a default allocator, let's continue to
181  // look inside the argument list.
182  int k = TClassEdit::IsSTLCont(fElements[1].c_str(),testAlloc);
183  if (k<0) kind = -kind;
184 
185  }
186  }
187 
188  // We return a negative value for anything which is not a vector or a list.
189  if(kind>2) kind = - kind;
190  return kind;
191 }
192 #include <iostream>
193 ////////////////////////////////////////////////////////////////////////////////
194 //////////////////////////////////////////////////////////////////////////////
195 /// Return the absolute type of typeDesc into the string answ.
196 
197 void TClassEdit::TSplitType::ShortType(std::string &answ, int mode)
198 {
199  // E.g.: typeDesc = "class const volatile TNamed**", returns "TNamed**".
200  // if (mode&1) remove last "*"s returns "TNamed"
201  // if (mode&2) remove default allocators from STL containers
202  // if (mode&4) remove all allocators from STL containers
203  // if (mode&8) return inner class of stl container. list<innerClass>
204  // if (mode&16) return deepest class of stl container. vector<list<deepest>>
205  // if (mode&kDropAllDefault) remove default template arguments
206  /////////////////////////////////////////////////////////////////////////////
207 
208  answ.clear();
209  int narg = fElements.size();
210  int tailLoc = 0;
211 
212  if (narg == 0) {
213  answ = fName;
214  return ;
215  }
216  // fprintf(stderr,"calling ShortType %d for %s with narg %d\n",mode,typeDesc,narg);
217  // {for (int i=0;i<narg;i++) fprintf(stderr,"calling ShortType %d for %s with %d %s \n",
218  // mode,typeDesc,i,arglist[i].c_str());
219  // }
220  if (fElements[narg-1].empty() == false &&
221  (fElements[narg-1][0]=='*'
222  || fElements[narg-1][0]=='&'
223  || fElements[narg-1][0]=='['
224  || 0 == fElements[narg-1].compare(0,6,"const*")
225  || 0 == fElements[narg-1].compare(0,6,"const&")
226  || 0 == fElements[narg-1].compare(0,6,"const[")
227  || 0 == fElements[narg-1].compare("const")
228  )
229  ) {
230  if ((mode&1)==0) tailLoc = narg-1;
231  }
232  else { assert(fElements[narg-1].empty()); };
233  narg--;
234  mode &= (~1);
235 
236  if (fNestedLocation) narg--;
237 
238  // fprintf(stderr,"calling ShortType %d for %s with narg %d tail %d\n",imode,typeDesc,narg,tailLoc);
239 
240  //kind of stl container
241  const int kind = STLKind(fElements[0]);
242  const int iall = STLArgs(kind);
243 
244  // Only class is needed
245  if (mode&(8|16)) {
246  while(narg-1>iall) { fElements.pop_back(); narg--;}
247  if (!fElements[0].empty() && tailLoc) {
248  tailLoc = 0;
249  }
250  fElements[0].clear();
251  mode&=(~8);
252  }
253 
254  if (mode & kDropAllDefault) mode |= kDropStlDefault;
255  if (mode & kDropStlDefault) mode |= kDropDefaultAlloc;
256 
257  if (kind) {
258  bool allocRemoved = false;
259 
260  if ( mode & (kDropDefaultAlloc|kDropAlloc) ) {
261  // remove allocators
262 
263 
264  if (narg-1 == iall+1) {
265  // has an allocator specified
266  bool dropAlloc = false;
267  if (mode & kDropAlloc) {
268 
269  dropAlloc = true;
270 
271  } else if (mode & kDropDefaultAlloc) {
272  switch (kind) {
273  case ROOT::kSTLvector:
274  case ROOT::kSTLlist:
276  case ROOT::kSTLdeque:
277  case ROOT::kSTLset:
278  case ROOT::kSTLmultiset:
281  dropAlloc = IsDefAlloc(fElements[iall+1].c_str(),fElements[1].c_str());
282  break;
283  case ROOT::kSTLmap:
284  case ROOT::kSTLmultimap:
287  dropAlloc = IsDefAlloc(fElements[iall+1].c_str(),fElements[1].c_str(),fElements[2].c_str());
288  break;
289  default:
290  dropAlloc = false;
291  }
292 
293  }
294  if (dropAlloc) {
295  narg--;
296  allocRemoved = true;
297  }
298  } else {
299  // has no allocator specified (hence it is already removed!)
300  allocRemoved = true;
301  }
302  }
303 
304  if ( allocRemoved && (mode & kDropStlDefault) && narg-1 == iall) { // remove default comparator
305  if ( IsDefComp( fElements[iall].c_str(), fElements[1].c_str() ) ) {
306  narg--;
307  }
308  } else if ( mode & kDropComparator ) {
309 
310  switch (kind) {
311  case ROOT::kSTLvector:
312  case ROOT::kSTLlist:
314  case ROOT::kSTLdeque:
315  break;
316  case ROOT::kSTLset:
317  case ROOT::kSTLmultiset:
318  case ROOT::kSTLmap:
319  case ROOT::kSTLmultimap:
320  if (!allocRemoved && narg-1 == iall+1) {
321  narg--;
322  allocRemoved = true;
323  }
324  if (narg-1 == iall) narg--;
325  break;
326  default:
327  break;
328  }
329  }
330 
331  // Treat now Pred and Hash for unordered set/map containers. Signature is:
332  // template < class Key,
333  // class Hash = hash<Key>,
334  // class Pred = equal_to<Key>,
335  // class Alloc = allocator<Key>
336  // > class unordered_{set,multiset}
337  // template < class Key,
338  // class Val,
339  // class Hash = hash<Key>,
340  // class Pred = equal_to<Key>,
341  // class Alloc = allocator<Key>
342  // > class unordered_{map,multimap}
343 
344 
346 
347  bool predRemoved = false;
348 
349  if ( allocRemoved && (mode & kDropStlDefault) && narg-1 == iall) { // remove default predicate
350  if ( IsDefPred( fElements[iall].c_str(), fElements[1].c_str() ) ) {
351  predRemoved=true;
352  narg--;
353  }
354  }
355 
356  if ( predRemoved && (mode & kDropStlDefault) && narg == iall) { // remove default hash
357  if ( IsDefHash( fElements[iall-1].c_str(), fElements[1].c_str() ) ) {
358  narg--;
359  }
360  }
361  }
362  } // End of treatment of stl containers
363  else {
364  if ( (mode & kDropStlDefault) && (narg >= 3)) {
365  unsigned int offset = (0==strncmp("const ",fElements[0].c_str(),6)) ? 6 : 0;
366  offset += (0==strncmp("std::",fElements[0].c_str()+offset,5)) ? 5 : 0;
367  if (0 == strcmp(fElements[0].c_str()+offset,"__shared_ptr"))
368  {
369 #ifdef _CONCURRENCE_H
370  static const std::string sharedPtrDef = std::to_string(__gnu_cxx::__default_lock_policy); // to_string is C++11
371 #else
372  static const std::string sharedPtrDef = std::to_string(2); // to_string is C++11
373 #endif
374  if (fElements[2] == sharedPtrDef) {
375  narg--;
376  }
377  }
378  }
379  }
380 
381  // do the same for all inside
382  for (int i=1;i<narg; i++) {
383  if (strchr(fElements[i].c_str(),'<')==0) {
384  if (mode&kDropStd) {
385  unsigned int offset = (0==strncmp("const ",fElements[i].c_str(),6)) ? 6 : 0;
386  RemoveStd( fElements[i], offset );
387  }
388  if (mode&kResolveTypedef) {
389  fElements[i] = ResolveTypedef(fElements[i].c_str(),true);
390  }
391  continue;
392  }
393  fElements[i] = TClassEdit::ShortType(fElements[i].c_str(),mode | TClassEdit::kKeepOuterConst);
394  if (mode&kResolveTypedef) {
395  // We 'just' need to check whether the outer type is a typedef or not;
396  // this also will add the default template parameter if any needs to
397  // be added.
398  string typeresult;
399  if (gInterpreterHelper->ExistingTypeCheck(fElements[i], typeresult)
400  || gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(fElements[i], typeresult)) {
401  if (!typeresult.empty()) fElements[i] = typeresult;
402  }
403  }
404  }
405 
406  unsigned int tailOffset = 0;
407  if (tailLoc && fElements[tailLoc].compare(0,5,"const") == 0) {
408  if (mode & kKeepOuterConst) answ += "const ";
409  tailOffset = 5;
410  }
411  if (!fElements[0].empty()) {answ += fElements[0]; answ +="<";}
412 
413 #if 0
414  // This code is no longer use, the moral equivalent would be to get
415  // the 'fixed' number of argument the user told us to ignore and drop those.
416  // However, the name we get here might be (usually) normalized enough that
417  // this is not necessary (at the very least nothing break in roottest without
418  // the aforementioned new code or this old code).
419  if (mode & kDropAllDefault) {
420  int nargNonDefault = 0;
421  std::string nonDefName = answ;
422  // "superlong" because tLong might turn fName into an even longer name
423  std::string nameSuperLong = fName;
424  if (gInterpreterHelper)
425  gInterpreterHelper->GetPartiallyDesugaredName(nameSuperLong);
426  while (++nargNonDefault < narg) {
427  // If T<a> is a "typedef" (aka default template params)
428  // to T<a,b> then we can strip the "b".
429  const char* closeTemplate = " >";
430  if (nonDefName[nonDefName.length() - 1] != '>')
431  ++closeTemplate;
432  string nondef = nonDefName + closeTemplate;
433  if (gInterpreterHelper &&
434  gInterpreterHelper->IsAlreadyPartiallyDesugaredName(nondef, nameSuperLong))
435  break;
436  if (nargNonDefault>1) nonDefName += ",";
437  nonDefName += fElements[nargNonDefault];
438  }
439  if (nargNonDefault < narg)
440  narg = nargNonDefault;
441  }
442 #endif
443 
444  { for (int i=1;i<narg-1; i++) { answ += fElements[i]; answ+=",";} }
445  if (narg>1) { answ += fElements[narg-1]; }
446 
447  if (!fElements[0].empty()) {
448  if ( answ.at(answ.size()-1) == '>') {
449  answ += " >";
450  } else {
451  answ += '>';
452  }
453  }
454  if (fNestedLocation) {
455  // Treat X pf A<B>::X
456  fElements[fNestedLocation] = TClassEdit::ShortType(fElements[fNestedLocation].c_str(),mode);
457  answ += fElements[fNestedLocation];
458  }
459  // tail is not a type name, just [2], &, * etc.
460  if (tailLoc) answ += fElements[tailLoc].c_str()+tailOffset;
461 }
462 
463 ////////////////////////////////////////////////////////////////////////////////
464 /// Converts STL container name to number. vector -> 1, etc..
465 /// If len is greater than 0, only look at that many characters in the string.
466 
468 {
469  unsigned char offset = 0;
470  if (type.compare(0,6,"const ")==0) { offset += 6; }
471  offset += StdLen(type.substr(offset));
472 
473  //container names
474  static const char *stls[] =
475  { "any", "vector", "list", "deque", "map", "multimap", "set", "multiset", "bitset",
476  "forward_list", "unordered_set", "unordered_multiset", "unordered_map", "unordered_multimap", 0};
477  static const size_t stllen[] =
478  { 3, 6, 4, 5, 3, 8, 3, 8, 6,
479  12, 13, 18, 13, 18, 0};
480  static const ROOT::ESTLType values[] =
486  // New C++11
491  };
492 
493  // kind of stl container
494  auto len = type.length();
495  if (len) {
496  len -= offset;
497  for(int k=1;stls[k];k++) {
498  if (len == stllen[k]) {
499  if (type.compare(offset,len,stls[k])==0) return values[k];
500  }
501  }
502  } else {
503  for(int k=1;stls[k];k++) {if (type.compare(offset,len,stls[k])==0) return values[k];}
504  }
505  return ROOT::kNotSTL;
506 }
507 
508 ////////////////////////////////////////////////////////////////////////////////
509 /// Return number of arguments for STL container before allocator
510 
511 int TClassEdit::STLArgs(int kind)
512 {
513  static const char stln[] =// min number of container arguments
514  // vector, list, deque, map, multimap, set, multiset, bitset,
515  { 1, 1, 1, 1, 3, 3, 2, 2, 1,
516  // forward_list, unordered_set, unordered_multiset, unordered_map, unordered_multimap
517  1, 3, 3, 4, 4};
518 
519  return stln[kind];
520 }
521 
522 ////////////////////////////////////////////////////////////////////////////////
523 
524 static size_t findNameEnd(const std::string_view full)
525 {
526  int level = 0;
527  for(size_t i = 0; i < full.length(); ++i) {
528  switch(full[i]) {
529  case '<': { ++level; break; }
530  case '>': {
531  if (level == 0) return i;
532  else --level;
533  break;
534  }
535  case ',': {
536  if (level == 0) return i;
537  break;
538  }
539  default: break;
540  }
541  }
542  return full.length();
543 }
544 
545 ////////////////////////////////////////////////////////////////////////////////
546 
547 static size_t findNameEnd(const std::string &full, size_t pos)
548 {
549  return pos + findNameEnd( {full.data()+pos,full.length()-pos} );
550 }
551 
552 ////////////////////////////////////////////////////////////////////////////////
553 /// return whether or not 'allocname' is the STL default allocator for type
554 /// 'classname'
555 
556 bool TClassEdit::IsDefAlloc(const char *allocname, const char *classname)
557 {
558  string_view a( allocname );
559  RemoveStd(a);
560 
561  if (a=="alloc") return true;
562  if (a=="__default_alloc_template<true,0>") return true;
563  if (a=="__malloc_alloc_template<0>") return true;
564 
565  const static int alloclen = strlen("allocator<");
566  if (a.compare(0,alloclen,"allocator<") != 0) {
567  return false;
568  }
569  a.remove_prefix(alloclen);
570 
571  RemoveStd(a);
572 
573  string_view k = classname;
574  RemoveStd(k);
575 
576  if (a.compare(0,k.length(),k) != 0) {
577  // Now we need to compare the normalized name.
578  size_t end = findNameEnd(a);
579 
580  std::string valuepart;
581  GetNormalizedName(valuepart,std::string_view(a.data(),end));
582 
583  std::string norm_value;
584  GetNormalizedName(norm_value,k);
585 
586  if (valuepart != norm_value) {
587  return false;
588  }
589  a.remove_prefix(end);
590  } else {
591  a.remove_prefix(k.length());
592  }
593 
594  if (a.compare(0,1,">")!=0 && a.compare(0,2," >")!=0) {
595  return false;
596  }
597 
598  return true;
599 }
600 
601 ////////////////////////////////////////////////////////////////////////////////
602 /// return whether or not 'allocname' is the STL default allocator for a key
603 /// of type 'keyclassname' and a value of type 'valueclassname'
604 
605 bool TClassEdit::IsDefAlloc(const char *allocname,
606  const char *keyclassname,
607  const char *valueclassname)
608 {
609  if (IsDefAlloc(allocname,keyclassname)) return true;
610 
611  string_view a( allocname );
612  RemoveStd(a);
613 
614  const static int alloclen = strlen("allocator<");
615  if (a.compare(0,alloclen,"allocator<") != 0) {
616  return false;
617  }
618  a.remove_prefix(alloclen);
619 
620  RemoveStd(a);
621 
622  const static int pairlen = strlen("pair<");
623  if (a.compare(0,pairlen,"pair<") != 0) {
624  return false;
625  }
626  a.remove_prefix(pairlen);
627 
628  const static int constlen = strlen("const");
629  if (a.compare(0,constlen+1,"const ") == 0) {
630  a.remove_prefix(constlen+1);
631  }
632 
633  RemoveStd(a);
634 
635  string_view k = keyclassname;
636  RemoveStd(k);
637  if (k.compare(0,constlen+1,"const ") == 0) {
638  k.remove_prefix(constlen+1);
639  }
640 
641  if (a.compare(0,k.length(),k) != 0) {
642  // Now we need to compare the normalized name.
643  size_t end = findNameEnd(a);
644 
645  std::string alloc_keypart;
646  GetNormalizedName(alloc_keypart,std::string_view(a.data(),end));
647 
648  std::string norm_key;
649  GetNormalizedName(norm_key,k);
650 
651  if (alloc_keypart != norm_key) {
652  if ( norm_key[norm_key.length()-1] == '*' ) {
653  // also check with a trailing 'const'.
654  norm_key += "const";
655  } else {
656  norm_key += " const";
657  }
658  if (alloc_keypart != norm_key) {
659  return false;
660  }
661  }
662  a.remove_prefix(end);
663  } else {
664  size_t end = k.length();
665  if ( (a[end-1] == '*') || a[end]==' ' ) {
666  size_t skipSpace = (a[end] == ' ');
667  if (a.compare(end+skipSpace,constlen,"const") == 0) {
668  end += constlen+skipSpace;
669  }
670  }
671  a.remove_prefix(end);
672  }
673 
674  if (a[0] != ',') {
675  return false;
676  }
677  a.remove_prefix(1);
678  RemoveStd(a);
679 
680  string_view v = valueclassname;
681  RemoveStd(v);
682 
683  if (a.compare(0,v.length(),v) != 0) {
684  // Now we need to compare the normalized name.
685  size_t end = findNameEnd(a);
686 
687  std::string valuepart;
688  GetNormalizedName(valuepart,std::string_view(a.data(),end));
689 
690  std::string norm_value;
691  GetNormalizedName(norm_value,v);
692 
693  if (valuepart != norm_value) {
694  return false;
695  }
696  a.remove_prefix(end);
697  } else {
698  a.remove_prefix(v.length());
699  }
700 
701  if (a.compare(0,1,">")!=0 && a.compare(0,2," >")!=0) {
702  return false;
703  }
704 
705  return true;
706 }
707 
708 ////////////////////////////////////////////////////////////////////////////////
709 /// return whether or not 'elementName' is the STL default Element for type
710 /// 'classname'
711 
712 static bool IsDefElement(const char *elementName, const char* defaultElementName, const char *classname)
713 {
714  string c = elementName;
715 
716  size_t pos = StdLen(c);
717 
718  const int elementlen = strlen(defaultElementName);
719  if (c.compare(pos,elementlen,defaultElementName) != 0) {
720  return false;
721  }
722  pos += elementlen;
723 
724  string k = classname;
725  if (c.compare(pos,k.length(),k) != 0) {
726  // Now we need to compare the normalized name.
727  size_t end = findNameEnd(c,pos);
728 
729  std::string keypart;
730  TClassEdit::GetNormalizedName(keypart,std::string_view(c.c_str()+pos,end-pos));
731 
732  std::string norm_key;
733  TClassEdit::GetNormalizedName(norm_key,k.c_str());
734 
735  if (keypart != norm_key) {
736  return false;
737  }
738  pos = end;
739  } else {
740  pos += k.length();
741  }
742 
743  if (c.compare(pos,1,">")!=0 && c.compare(pos,2," >")!=0) {
744  return false;
745  }
746 
747  return true;
748 }
749 
750 ////////////////////////////////////////////////////////////////////////////////
751 /// return whether or not 'compare' is the STL default comparator for type
752 /// 'classname'
753 
754 bool TClassEdit::IsDefComp(const char *compname, const char *classname)
755 {
756  return IsDefElement(compname, "less<", classname);
757 }
758 
759 ////////////////////////////////////////////////////////////////////////////////
760 /// return whether or not 'predname' is the STL default predicate for type
761 /// 'classname'
762 
763 bool TClassEdit::IsDefPred(const char *predname, const char *classname)
764 {
765  return IsDefElement(predname, "equal_to<", classname);
766 }
767 
768 ////////////////////////////////////////////////////////////////////////////////
769 /// return whether or not 'hashname' is the STL default hash for type
770 /// 'classname'
771 
772 bool TClassEdit::IsDefHash(const char *hashname, const char *classname)
773 {
774  return IsDefElement(hashname, "hash<", classname);
775 }
776 
777 ////////////////////////////////////////////////////////////////////////////////
778 /// Return the normalized name. See TMetaUtils::GetNormalizedName.
779 ///
780 /// Return the type name normalized for ROOT,
781 /// keeping only the ROOT opaque typedef (Double32_t, etc.) and
782 /// removing the STL collections default parameter if any.
783 ///
784 /// Compare to TMetaUtils::GetNormalizedName, this routines does not
785 /// and can not add default template parameters.
786 
787 void TClassEdit::GetNormalizedName(std::string &norm_name, std::string_view name)
788 {
789  norm_name = std::string(name); // NOTE: Is that the shortest version?
790 
791  // Remove the std:: and default template argument and insert the Long64_t and change basic_string to string.
794 
795  // Depending on how the user typed their code, in particular typedef
796  // declarations, we may end up with an explicit '::' being
797  // part of the result string. For consistency, we must remove it.
798  if (norm_name.length()>2 && norm_name[0]==':' && norm_name[1]==':') {
799  norm_name.erase(0,2);
800  }
801 
802  if (gInterpreterHelper) {
803  // See if the expanded name itself is a typedef.
804  std::string typeresult;
805  if (gInterpreterHelper->ExistingTypeCheck(norm_name, typeresult)
806  || gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(norm_name, typeresult)) {
807 
808  if (!typeresult.empty()) norm_name = typeresult;
809  }
810  }
811 }
812 
813 ////////////////////////////////////////////////////////////////////////////////
814 /// Replace 'long long' and 'unsigned long long' by 'Long64_t' and 'ULong64_t'
815 
816 string TClassEdit::GetLong64_Name(const char* original)
817 {
818  if (original==0)
819  return "";
820  else
821  return GetLong64_Name(string(original));
822 }
823 
824 ////////////////////////////////////////////////////////////////////////////////
825 /// Replace 'long long' and 'unsigned long long' by 'Long64_t' and 'ULong64_t'
826 
827 string TClassEdit::GetLong64_Name(const string& original)
828 {
829  static const char* longlong_s = "long long";
830  static const char* ulonglong_s = "unsigned long long";
831  static const unsigned int longlong_len = strlen(longlong_s);
832  static const unsigned int ulonglong_len = strlen(ulonglong_s);
833 
834  string result = original;
835 
836  int pos = 0;
837  while( (pos = result.find(ulonglong_s,pos) ) >=0 ) {
838  result.replace(pos, ulonglong_len, "ULong64_t");
839  }
840  pos = 0;
841  while( (pos = result.find(longlong_s,pos) ) >=0 ) {
842  result.replace(pos, longlong_len, "Long64_t");
843  }
844  return result;
845 }
846 
847 ////////////////////////////////////////////////////////////////////////////////
848 /// Return the start of the unqualified name include in 'original'.
849 
850 const char *TClassEdit::GetUnqualifiedName(const char *original)
851 {
852  const char *lastPos = original;
853  {
854  long depth = 0;
855  for(auto cursor = original; *cursor != '\0'; ++cursor) {
856  if ( *cursor == '<' || *cursor == '(') ++depth;
857  else if ( *cursor == '>' || *cursor == ')' ) --depth;
858  else if ( *cursor == ':' ) {
859  if (depth==0 && *(cursor+1) == ':' && *(cursor+2) != '\0') {
860  lastPos = cursor+2;
861  }
862  }
863  }
864  }
865  return lastPos;
866 }
867 
868 ////////////////////////////////////////////////////////////////////////////////
869 
870 static void R__FindTrailing(std::string &full, /*modified*/
871  std::string &stars /* the literal output */
872  )
873 {
874  const char *t = full.c_str();
875  const unsigned int tlen( full.size() );
876 
877  const char *starloc = t + tlen - 1;
878  bool hasconst = false;
879  if ( (*starloc)=='t'
880  && (starloc-t) > 4 && 0 == strncmp((starloc-4),"const",5)
881  && ( (*(starloc-5)) == ' ' || (*(starloc-5)) == '*' || (*(starloc-5)) == '&'
882  || (*(starloc-5)) == '>' || (*(starloc-5)) == ']') ) {
883  // we are ending on a const.
884  starloc -= 4;
885  if ((*starloc-1)==' ') {
886  // Take the space too.
887  starloc--;
888  }
889  hasconst = true;
890  }
891  if ( hasconst || (*starloc)=='*' || (*starloc)=='&' || (*starloc)==']' ) {
892  bool isArray = ( (*starloc)==']' );
893  while( t<=(starloc-1) && ((*(starloc-1))=='*' || (*(starloc-1))=='&' || (*(starloc-1))=='t' || isArray)) {
894  if (isArray) {
895  starloc--;
896  isArray = ! ( (*starloc)=='[' );
897  } else if ( (*(starloc-1))=='t' ) {
898  if ( (starloc-1-t) > 5 && 0 == strncmp((starloc-5),"const",5)
899  && ( (*(starloc-6)) == ' ' || (*(starloc-6)) == '*' || (*(starloc-6)) == '&'
900  || (*(starloc-6)) == '>' || (*(starloc-6)) == ']')) {
901  // we have a const.
902  starloc -= 5;
903  } else {
904  break;
905  }
906  } else {
907  starloc--;
908  }
909  }
910  stars = starloc;
911  if ((*(starloc-1))==' ') {
912  // erase the space too.
913  starloc--;
914  }
915 
916  const unsigned int starlen = strlen(starloc);
917  full.erase(tlen-starlen,starlen);
918  } else if (hasconst) {
919  stars = starloc;
920  const unsigned int starlen = strlen(starloc);
921  full.erase(tlen-starlen,starlen);
922  }
923 
924 }
925 
926 ////////////////////////////////////////////////////////////////////////////////
927 ////////////////////////////////////////////////////////////////////////////
928 /// Stores in output (after emptying it) the splited type.
929 /// Stores the location of the tail (nested names) in nestedLoc (0 indicates no tail).
930 /// Return the number of elements stored.
931 ///
932 /// First in list is the template name or is empty
933 /// "vector<list<int>,alloc>**" to "vector" "list<int>" "alloc" "**"
934 /// or "TNamed*" to "" "TNamed" "*"
935 ////////////////////////////////////////////////////////////////////////////
936 
937 int TClassEdit::GetSplit(const char *type, vector<string>& output, int &nestedLoc, EModType mode)
938 {
939  nestedLoc = 0;
940  output.clear();
941  if (strlen(type)==0) return 0;
942 
943  int cleantypeMode = 1 /* keepInnerConst */;
944  if (mode & kKeepOuterConst) {
945  cleantypeMode = 0; /* remove only the outer class keyword */
946  }
947  string full( mode & kLong64 ? TClassEdit::GetLong64_Name( CleanType(type, cleantypeMode) )
948  : CleanType(type, cleantypeMode) );
949 
950  // We need to replace basic_string with string.
951  {
952  unsigned int const_offset = (0==strncmp("const ",full.c_str(),6)) ? 6 : 0;
953  bool isString = false;
954  bool isStdString = false;
955  static const char* basic_string_std = "std::basic_string<char";
956  static const unsigned int basic_string_std_len = strlen(basic_string_std);
957 
958  if (full.compare(const_offset,basic_string_std_len,basic_string_std) == 0
959  && full.size() > basic_string_std_len) {
960  isString = true;
961  isStdString = true;
962  } else if (full.compare(const_offset,basic_string_std_len-5,basic_string_std+5) == 0
963  && full.size() > (basic_string_std_len-5)) {
964  // no std.
965  isString = true;
966  }
967  if (isString) {
968  size_t offset = isStdString ? basic_string_std_len : basic_string_std_len - 5;
969  offset += const_offset;
970  if ( full[offset] == '>' ) {
971  // done.
972  } else if (full[offset] == ',') {
973  ++offset;
974  if (full.compare(offset, 5, "std::") == 0) {
975  offset += 5;
976  }
977  static const char* char_traits_s = "char_traits<char>";
978  static const unsigned int char_traits_len = strlen(char_traits_s);
979  if (full.compare(offset, char_traits_len, char_traits_s) == 0) {
980  offset += char_traits_len;
981  if ( full[offset] == '>') {
982  // done.
983  } else if (full[offset] == ' ' && full[offset+1] == '>') {
984  ++offset;
985  // done.
986  } else if (full[offset] == ',') {
987  ++offset;
988  if (full.compare(offset, 5, "std::") == 0) {
989  offset += 5;
990  }
991  static const char* allocator_s = "allocator<char>";
992  static const unsigned int allocator_len = strlen(allocator_s);
993  if (full.compare(offset, allocator_len, allocator_s) == 0) {
994  offset += allocator_len;
995  if ( full[offset] == '>') {
996  // done.
997  } else if (full[offset] == ' ' && full[offset+1] == '>') {
998  ++offset;
999  // done.
1000  } else {
1001  // Not std::string
1002  isString = false;
1003  }
1004  }
1005  } else {
1006  // Not std::string
1007  isString = false;
1008  }
1009  } else {
1010  // Not std::string.
1011  isString = false;
1012  }
1013  } else {
1014  // Not std::string.
1015  isString = false;
1016  }
1017  if (isString) {
1018  output.push_back(string());
1019  if (const_offset && (mode & kKeepOuterConst)) {
1020  if (isStdString && !(mode & kDropStd)) {
1021  output.push_back("const std::string");
1022  } else {
1023  output.push_back("const string");
1024  }
1025  } else {
1026  if (isStdString && !(mode & kDropStd)) {
1027  output.push_back("std::string");
1028  } else {
1029  output.push_back("string");
1030  }
1031  }
1032  if (offset < full.length()) {
1033  // Copy the trailing text.
1034  // keep the '>' inside right for R__FindTrailing to work
1035  string right( full.substr(offset) );
1036  string stars;
1037  R__FindTrailing(right, stars);
1038  output.back().append(right.c_str()+1); // skip the '>'
1039  output.push_back(stars);
1040  } else {
1041  output.push_back("");
1042  }
1043  return output.size();
1044  }
1045  }
1046  }
1047 
1048  if ( mode & kDropStd) {
1049  unsigned int offset = (0==strncmp("const ",full.c_str(),6)) ? 6 : 0;
1050  RemoveStd( full, offset );
1051  }
1052 
1053  string stars;
1054  if ( !full.empty() ) {
1055  R__FindTrailing(full, stars);
1056  }
1057 
1058  const char *c = strchr(full.c_str(),'<');
1059  if (c) {
1060  //we have 'something<'
1061  output.push_back(string(full,0,c - full.c_str()));
1062 
1063  const char *cursor;
1064  int level = 0;
1065  for(cursor = c + 1; *cursor != '\0' && !(level==0 && *cursor == '>'); ++cursor) {
1066  switch (*cursor) {
1067  case '<': ++level; break;
1068  case '>': --level; break;
1069  case ',':
1070  if (level == 0) {
1071  output.push_back(std::string(c+1,cursor));
1072  c = cursor;
1073  }
1074  break;
1075  }
1076  }
1077  if (*cursor=='>') {
1078  if (*(cursor-1) == ' ') {
1079  output.push_back(std::string(c+1,cursor-1));
1080  } else {
1081  output.push_back(std::string(c+1,cursor));
1082  }
1083  // See what's next!
1084  if (*(cursor+1)==':') {
1085  // we have a name specified inside the class/namespace
1086  // For now we keep it in one piece
1087  nestedLoc = output.size();
1088  output.push_back((cursor+1));
1089  }
1090  } else if (level >= 0) {
1091  // Unterminated template
1092  output.push_back(std::string(c+1,cursor));
1093  }
1094  } else {
1095  //empty
1096  output.push_back(string());
1097  output.push_back(full);
1098  }
1099 
1100  if (!output.empty()) output.push_back(stars);
1101  return output.size();
1102 }
1103 
1104 
1105 ////////////////////////////////////////////////////////////////////////////////
1106 ////////////////////////////////////////////////////////////////////////////
1107 /// Cleanup type description, redundant blanks removed
1108 /// and redundant tail ignored
1109 /// return *tail = pointer to last used character
1110 /// if (mode==0) keep keywords
1111 /// if (mode==1) remove keywords outside the template params
1112 /// if (mode>=2) remove the keywords everywhere.
1113 /// if (tail!=0) cut before the trailing *
1114 ///
1115 /// The keywords currently are: "const" , "volatile" removed
1116 ///
1117 ///
1118 /// CleanType(" A<B, C< D, E> > *,F,G>") returns "A<B,C<D,E> >*"
1119 ////////////////////////////////////////////////////////////////////////////
1120 
1121 string TClassEdit::CleanType(const char *typeDesc, int mode, const char **tail)
1122 {
1123  static const char* remove[] = {"class","const","volatile",0};
1124  static bool isinit = false;
1125  static std::vector<size_t> lengths;
1126  if (!isinit) {
1127  for (int k=0; remove[k]; ++k) {
1128  lengths.push_back(strlen(remove[k]));
1129  }
1130  isinit = true;
1131  }
1132 
1133  string result;
1134  result.reserve(strlen(typeDesc)*2);
1135  int lev=0,kbl=1;
1136  const char* c;
1137 
1138  for(c=typeDesc;*c;c++) {
1139  if (c[0]==' ') {
1140  if (kbl) continue;
1141  if (!isalnum(c[ 1]) && c[ 1] !='_') continue;
1142  }
1143  if (kbl && (mode>=2 || lev==0)) { //remove "const' etc...
1144  int done = 0;
1145  int n = (mode) ? 999 : 1;
1146 
1147  // loop on all the keywords we want to remove
1148  for (int k=0; k<n && remove[k]; k++) {
1149  int rlen = lengths[k];
1150 
1151  // Do we have a match
1152  if (strncmp(remove[k],c,rlen)) continue;
1153 
1154  // make sure that the 'keyword' is not part of a longer indentifier
1155  if (isalnum(c[rlen]) || c[rlen]=='_' || c[rlen]=='$') continue;
1156 
1157  c+=rlen-1; done = 1; break;
1158  }
1159  if (done) continue;
1160  }
1161 
1162  kbl = (!isalnum(c[ 0]) && c[ 0]!='_' && c[ 0]!='$' && c[0]!='[' && c[0]!=']' && c[0]!='-' && c[0]!='@');
1163  // '@' is special character used only the artifical class name used by ROOT to implement the
1164  // I/O customization rules that requires caching of the input data.
1165 
1166  if (*c == '<' || *c == '(') lev++;
1167  if (lev==0 && !isalnum(*c)) {
1168  if (!strchr("*&:._$ []-@",*c)) break;
1169  // '.' is used as a module/namespace separator by PyROOT, see
1170  // TPyClassGenerator::GetClass.
1171  }
1172  if (c[0]=='>' && result.size() && result[result.size()-1]=='>') result+=" ";
1173 
1174  result += c[0];
1175 
1176  if (*c == '>' || *c == ')') lev--;
1177  }
1178  if(tail) *tail=c;
1179  return result;
1180 }
1181 
1182 ////////////////////////////////////////////////////////////////////////////////
1183 //////////////////////////////////////////////////////////////////////////////
1184 /// Return the absolute type of typeDesc.
1185 /// E.g.: typeDesc = "class const volatile TNamed**", returns "TNamed**".
1186 /// if (mode&1) remove last "*"s returns "TNamed"
1187 /// if (mode&2) remove default allocators from STL containers
1188 /// if (mode&4) remove all allocators from STL containers
1189 /// if (mode&8) return inner class of stl container. list<innerClass>
1190 /// if (mode&16) return deapest class of stl container. vector<list<deapest>>
1191 /// if (mode&kDropAllDefault) remove default template arguments
1192 //////////////////////////////////////////////////////////////////////////////
1193 
1194 string TClassEdit::ShortType(const char *typeDesc, int mode)
1195 {
1196  string answer;
1197 
1198  // get list of all arguments
1199  if (typeDesc) {
1200  TSplitType arglist(typeDesc, (EModType) mode);
1201  arglist.ShortType(answer, mode);
1202  }
1203 
1204  return answer;
1205 }
1206 
1207 ////////////////////////////////////////////////////////////////////////////////
1208 /// Return true if the type is one the interpreter details which are
1209 /// only forward declared (ClassInfo_t etc..)
1210 
1212 {
1213  size_t len = strlen(type);
1214  if (len < 2 || strncmp(type+len-2,"_t",2) != 0) return false;
1215 
1216  unsigned char offset = 0;
1217  if (strncmp(type,"const ",6)==0) { offset += 6; }
1218  static const char *names[] = { "CallFunc_t","ClassInfo_t","BaseClassInfo_t",
1219  "DataMemberInfo_t","FuncTempInfo_t","MethodInfo_t","MethodArgInfo_t",
1220  "TypeInfo_t","TypedefInfo_t",0};
1221 
1222  for(int k=1;names[k];k++) {if (strcmp(type+offset,names[k])==0) return true;}
1223  return false;
1224 }
1225 
1226 ////////////////////////////////////////////////////////////////////////////////
1227 /// Return true is the name is std::bitset<number> or bitset<number>
1228 
1229 bool TClassEdit::IsSTLBitset(const char *classname)
1230 {
1231  size_t offset = StdLen(classname);
1232  if ( strncmp(classname+offset,"bitset<",strlen("bitset<"))==0) return true;
1233  return false;
1234 }
1235 
1236 ////////////////////////////////////////////////////////////////////////////////
1237 /// Return the type of STL collection, if any, that is the underlying type
1238 /// of the given type. Namely return the value of IsSTLCont after stripping
1239 /// pointer, reference and constness from the type.
1240 /// UnderlyingIsSTLCont("vector<int>*") == IsSTLCont("vector<int>")
1241 /// See TClassEdit::IsSTLCont
1242 ///
1243 /// type : type name: vector<list<classA,allocator>,allocator>*
1244 /// result: 0 : not stl container
1245 /// code of container 1=vector,2=list,3=deque,4=map
1246 /// 5=multimap,6=set,7=multiset
1247 
1249 {
1250  if (type.compare(0,6,"const ",6) == 0)
1251  type.remove_prefix(6);
1252 
1253  while(type[type.length()-1]=='*' ||
1254  type[type.length()-1]=='&' ||
1255  type[type.length()-1]==' ') {
1256  type.remove_suffix(1);
1257  }
1258  return IsSTLCont(type);
1259 }
1260 
1261 ////////////////////////////////////////////////////////////////////////////////
1262 /// type : type name: vector<list<classA,allocator>,allocator>
1263 /// result: 0 : not stl container
1264 /// code of container 1=vector,2=list,3=deque,4=map
1265 /// 5=multimap,6=set,7=multiset
1266 
1268 {
1269  auto pos = type.find('<');
1270  if (pos==std::string_view::npos) return ROOT::kNotSTL;
1271 
1272  auto c = pos+1;
1273  for (decltype(type.length()) level = 1; c < type.length(); ++c) {
1274  if (type[c] == '<') ++level;
1275  if (type[c] == '>') --level;
1276  if (level == 0) break;
1277  }
1278  if (c != (type.length()-1) ) {
1279  return ROOT::kNotSTL;
1280  }
1281 
1282  return STLKind({type.data(),pos});
1283 }
1284 
1285 ////////////////////////////////////////////////////////////////////////////////
1286 /// type : type name: vector<list<classA,allocator>,allocator>
1287 /// testAlloc: if true, we test allocator, if it is not default result is negative
1288 /// result: 0 : not stl container
1289 /// abs(result): code of container 1=vector,2=list,3=deque,4=map
1290 /// 5=multimap,6=set,7=multiset
1291 /// positive val: we have a vector or list with default allocator to any depth
1292 /// like vector<list<vector<int>>>
1293 /// negative val: STL container other than vector or list, or non default allocator
1294 /// For example: vector<deque<int>> has answer -1
1295 
1296 int TClassEdit::IsSTLCont(const char *type, int testAlloc)
1297 {
1298  if (strchr(type,'<')==0) return 0;
1299 
1300  TSplitType arglist( type );
1301  return arglist.IsSTLCont(testAlloc);
1302 }
1303 
1304 ////////////////////////////////////////////////////////////////////////////////
1305 /// return true if the class belongs to the std namespace
1306 
1307 bool TClassEdit::IsStdClass(const char *classname)
1308 {
1309  classname += StdLen( classname );
1310  if ( strcmp(classname,"string")==0 ) return true;
1311  if ( strncmp(classname,"bitset<",strlen("bitset<"))==0) return true;
1312  if ( strncmp(classname,"pair<",strlen("pair<"))==0) return true;
1313  if ( strcmp(classname,"allocator")==0) return true;
1314  if ( strncmp(classname,"allocator<",strlen("allocator<"))==0) return true;
1315  if ( strncmp(classname,"greater<",strlen("greater<"))==0) return true;
1316  if ( strncmp(classname,"less<",strlen("less<"))==0) return true;
1317  if ( strncmp(classname,"equal_to<",strlen("equal_to<"))==0) return true;
1318  if ( strncmp(classname,"hash<",strlen("hash<"))==0) return true;
1319  if ( strncmp(classname,"auto_ptr<",strlen("auto_ptr<"))==0) return true;
1320 
1321  if ( strncmp(classname,"vector<",strlen("vector<"))==0) return true;
1322  if ( strncmp(classname,"list<",strlen("list<"))==0) return true;
1323  if ( strncmp(classname,"forward_list<",strlen("forward_list<"))==0) return true;
1324  if ( strncmp(classname,"deque<",strlen("deque<"))==0) return true;
1325  if ( strncmp(classname,"map<",strlen("map<"))==0) return true;
1326  if ( strncmp(classname,"multimap<",strlen("multimap<"))==0) return true;
1327  if ( strncmp(classname,"set<",strlen("set<"))==0) return true;
1328  if ( strncmp(classname,"multiset<",strlen("multiset<"))==0) return true;
1329  if ( strncmp(classname,"unordered_set<",strlen("unordered_set<"))==0) return true;
1330  if ( strncmp(classname,"unordered_multiset<",strlen("unordered_multiset<"))==0) return true;
1331  if ( strncmp(classname,"unordered_map<",strlen("unordered_map<"))==0) return true;
1332  if ( strncmp(classname,"unordered_multimap<",strlen("unordered_multimap<"))==0) return true;
1333  if ( strncmp(classname,"bitset<",strlen("bitset<"))==0) return true;
1334 
1335  return false;
1336 }
1337 
1338 
1339 ////////////////////////////////////////////////////////////////////////////////
1340 
1341 bool TClassEdit::IsVectorBool(const char *name) {
1342  TSplitType splitname( name );
1343 
1344  return ( TClassEdit::STLKind( splitname.fElements[0] ) == ROOT::kSTLvector)
1345  && ( splitname.fElements[1] == "bool" || splitname.fElements[1]=="Bool_t");
1346 }
1347 
1348 ////////////////////////////////////////////////////////////////////////////////
1349 
1350 static void ResolveTypedefProcessType(const char *tname,
1351  unsigned int /* len */,
1352  unsigned int cursor,
1353  bool constprefix,
1354  unsigned int start_of_type,
1355  unsigned int end_of_type,
1356  unsigned int mod_start_of_type,
1357  bool &modified,
1358  std::string &result)
1359 {
1360  std::string type(modified && (mod_start_of_type < result.length()) ?
1361  result.substr(mod_start_of_type, string::npos)
1362  : 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
1363  string typeresult;
1364  if (gInterpreterHelper->ExistingTypeCheck(type, typeresult)
1365  || gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(type, typeresult)) {
1366  // it is a known type
1367  if (!typeresult.empty()) {
1368  // and it is a typedef, we need to replace it in the output.
1369  if (modified) {
1370  result.replace(mod_start_of_type, string::npos,
1371  typeresult);
1372  }
1373  else {
1374  modified = true;
1375  mod_start_of_type = start_of_type;
1376  result += string(tname,0,start_of_type);
1377  if (constprefix && typeresult.compare(0,6,"const ",6) == 0) {
1378  result += typeresult.substr(6,string::npos);
1379  } else {
1380  result += typeresult;
1381  }
1382  }
1383  } else if (modified) {
1384  result.replace(mod_start_of_type, string::npos,
1385  type);
1386  }
1387  if (modified) {
1388  if (end_of_type != 0 && end_of_type!=cursor) {
1389  result += std::string(tname,end_of_type,cursor-end_of_type);
1390  }
1391  }
1392  } else {
1393  // no change needed.
1394  if (modified) {
1395  // result += type;
1396  if (end_of_type != 0 && end_of_type!=cursor) {
1397  result += std::string(tname,end_of_type,cursor-end_of_type);
1398  }
1399  }
1400  }
1401 }
1402 
1403 ////////////////////////////////////////////////////////////////////////////////
1404 
1405 static void ResolveTypedefImpl(const char *tname,
1406  unsigned int len,
1407  unsigned int &cursor,
1408  bool &modified,
1409  std::string &result)
1410 {
1411  // Need to parse and deal with
1412  // A::B::C< D, E::F, G::H<I,J>::K::L >::M
1413  // where E might be replace by N<O,P>
1414  // and G::H<I,J>::K or G might be a typedef.
1415 
1416  bool constprefix = false;
1417 
1418  if (tname[cursor]==' ') {
1419  if (!modified) {
1420  modified = true;
1421  result += string(tname,0,cursor);
1422  }
1423  while (tname[cursor]==' ') ++cursor;
1424  }
1425 
1426  if (tname[cursor]=='c' && (cursor+6<len)) {
1427  if (strncmp(tname+cursor,"const ",6) == 0) {
1428  cursor += 6;
1429  if (modified) result += "const ";
1430  }
1431  constprefix = true;
1432 
1433  }
1434 
1435  // When either of those two is true, we should probably go to modified
1436  // mode. (Otherwise we rely on somebody else to strip the std::)
1437  if (len > 5 && strncmp(tname+cursor,"std::",5) == 0) {
1438  cursor += 5;
1439  }
1440  if (len > 2 && strncmp(tname+cursor,"::",2) == 0) {
1441  cursor += 2;
1442  len -= 2;
1443  }
1444 
1445  unsigned int start_of_type = cursor;
1446  unsigned int end_of_type = 0;
1447  unsigned int mod_start_of_type = result.length();
1448  unsigned int prevScope = cursor;
1449  for ( ; cursor<len; ++cursor) {
1450  switch (tname[cursor]) {
1451  case ':': {
1452  if ((cursor+1)>=len || tname[cursor+1]!=':') {
1453  // we expected another ':', malformed, give up.
1454  if (modified) result += (tname+prevScope);
1455  return;
1456  }
1457  string scope;
1458  if (modified) {
1459  scope = result.substr(mod_start_of_type, string::npos);
1460  scope += std::string(tname+prevScope,cursor-prevScope);
1461  } else {
1462  scope = std::string(tname, start_of_type, cursor - start_of_type); // we need to try to avoid this copy
1463  }
1464  std::string scoperesult;
1465  bool isInlined = false;
1466  if (gInterpreterHelper->ExistingTypeCheck(scope, scoperesult)
1467  ||gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(scope, scoperesult)) {
1468  // it is a known type
1469  if (!scoperesult.empty()) {
1470  // and it is a typedef
1471  if (modified) {
1472  if (constprefix && scoperesult.compare(0,6,"const ",6) != 0) mod_start_of_type -= 6;
1473  result.replace(mod_start_of_type, string::npos,
1474  scoperesult);
1475  result += "::";
1476  } else {
1477  modified = true;
1478  mod_start_of_type = start_of_type;
1479  result += string(tname,0,start_of_type);
1480  //if (constprefix) result += "const ";
1481  result += scoperesult;
1482  result += "::";
1483  }
1484  } else if (modified) {
1485  result += std::string(tname+prevScope,cursor+1-prevScope);
1486  }
1487  } else if (!gInterpreterHelper->IsDeclaredScope(scope,isInlined)) {
1488  // the nesting namespace is not declared
1489  if (modified) result += (tname+prevScope);
1490  // Unfortunately, this is too harsh .. what about:
1491  // unknown::wrapper<Int_t>
1492  return;
1493  } else if (isInlined) {
1494  // humm ... just skip it.
1495  if (!modified) {
1496  modified = true;
1497  mod_start_of_type = start_of_type;
1498  result += string(tname,0,start_of_type);
1499  //if (constprefix) result += "const ";
1500  result += string(tname,start_of_type,prevScope - start_of_type);
1501  }
1502  } else if (modified) {
1503  result += std::string(tname+prevScope,cursor+1-prevScope);
1504  }
1505  // Consume the 1st semi colon, the 2nd will be consume by the for loop.
1506  ++cursor;
1507  prevScope = cursor+1;
1508  break;
1509  }
1510  case '<': {
1511  // push information on stack
1512  if (modified) {
1513  result += std::string(tname+prevScope,cursor+1-prevScope);
1514  // above includes the '<' .... result += '<';
1515  }
1516  do {
1517  ++cursor;
1518  ResolveTypedefImpl(tname,len,cursor,modified,result);
1519  } while( cursor<len && tname[cursor] == ',' );
1520 
1521  while (cursor<len && tname[cursor+1]==' ') ++cursor;
1522 
1523  // Since we already checked the type, skip the next section
1524  // (respective the scope section and final type processing section)
1525  // as they would re-do the same job.
1526  if (cursor+2<len && tname[cursor+1]==':' && tname[cursor+2]==':') {
1527  if (modified) result += "::";
1528  cursor += 2;
1529  prevScope = cursor+1;
1530  }
1531  if ( (cursor+1)<len && tname[cursor+1] == ',') {
1532  ++cursor;
1533  if (modified) result += ',';
1534  return;
1535  }
1536  if ( (cursor+1)<len && tname[cursor+1] == '>') {
1537  ++cursor;
1538  if (modified) result += " >";
1539  return;
1540  }
1541  if ( (cursor+1) >= len) {
1542  return;
1543  }
1544  if (tname[cursor] != ' ') break;
1545  if (modified) prevScope = cursor+1;
1546  // If the 'current' character is a space we need to treat it,
1547  // since this the next case statement, we can just fall through,
1548  // otherwise we should need to do:
1549  // --cursor; break;
1550  }
1551  case ' ': {
1552  end_of_type = cursor;
1553  // let's see if we have 'long long' or 'unsigned int' or 'signed char' or what not.
1554  while ((cursor+1)<len && tname[cursor+1] == ' ') ++cursor;
1555 
1556  auto next = cursor+1;
1557  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] == ']'))
1558  {
1559  // A first const after the type needs to be move in the front.
1560  if (!modified) {
1561  modified = true;
1562  result += string(tname,0,start_of_type);
1563  result += "const ";
1564  mod_start_of_type = start_of_type + 6;
1565  result += string(tname,start_of_type,end_of_type-start_of_type);
1566  } else if (mod_start_of_type < result.length()) {
1567  result.insert(mod_start_of_type,"const ");
1568  mod_start_of_type += 6;
1569  } else {
1570  result += "const ";
1571  mod_start_of_type += 6;
1572  result += string(tname,start_of_type,end_of_type-start_of_type);
1573  }
1574  cursor += 5;
1575  end_of_type = cursor+1;
1576  prevScope = end_of_type;
1577  if ((next+5)==len || tname[next+5] == ',' || tname[next+5] == '>' || tname[next+5] == '[') {
1578  break;
1579  }
1580  } else if (next!=len && tname[next] != '*' && tname[next] != '&') {
1581  // the type is not ended yet.
1582  end_of_type = 0;
1583  break;
1584  }
1585  ++cursor;
1586  // Intentional fall through;
1587  }
1588  case '*':
1589  case '&': {
1590  if (tname[cursor] != ' ') end_of_type = cursor;
1591  // check and skip const (followed by *,&, ,) ... what about followed by ':','['?
1592  auto next = cursor+1;
1593  if (strncmp(tname+next,"const",5) == 0) {
1594  if ((next+5)==len || tname[next+5] == ' ' || tname[next+5] == '*' || tname[next+5] == '&' || tname[next+5] == ',' || tname[next+5] == '>' || tname[next+5] == '[') {
1595  next += 5;
1596  }
1597  }
1598  while (next<len &&
1599  (tname[next] == ' ' || tname[next] == '*' || tname[next] == '&')) {
1600  ++next;
1601  // check and skip const (followed by *,&, ,) ... what about followed by ':','['?
1602  if (strncmp(tname+next,"const",5) == 0) {
1603  if ((next+5)==len || tname[next+5] == ' ' || tname[next+5] == '*' || tname[next+5] == '&' || tname[next+5] == ',' || tname[next+5] == '>' || tname[next+5] == '[') {
1604  next += 5;
1605  }
1606  }
1607  }
1608  cursor = next-1;
1609 // if (modified && mod_start_of_type < result.length()) {
1610 // result += string(tname,end_of_type,cursor-end_of_type);
1611 // }
1612  break;
1613  }
1614  case ',': {
1615  if (modified && prevScope) {
1616  result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1617  }
1618  ResolveTypedefProcessType(tname,len,cursor,constprefix,start_of_type,end_of_type,mod_start_of_type,
1619  modified, result);
1620  if (modified) result += ',';
1621  return;
1622  }
1623  case '>': {
1624  if (modified && prevScope) {
1625  result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1626  }
1627  ResolveTypedefProcessType(tname,len,cursor,constprefix,start_of_type,end_of_type,mod_start_of_type,
1628  modified, result);
1629  if (modified) result += '>';
1630  return;
1631  }
1632  default:
1633  end_of_type = 0;
1634  }
1635  }
1636 
1637  if (prevScope && modified) result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1638 
1639  ResolveTypedefProcessType(tname,len,cursor,constprefix,start_of_type,end_of_type,mod_start_of_type,
1640  modified, result);
1641 }
1642 
1643 
1644 ////////////////////////////////////////////////////////////////////////////////
1645 
1646 string TClassEdit::ResolveTypedef(const char *tname, bool /* resolveAll */)
1647 {
1648  // Return the name of type 'tname' with all its typedef components replaced
1649  // by the actual type its points to
1650  // For example for "typedef MyObj MyObjTypedef;"
1651  // vector<MyObjTypedef> return vector<MyObj>
1652  //
1653 
1654  if ( tname==0 || tname[0]==0 || !gInterpreterHelper) return "";
1655 
1656  std::string result;
1657 
1658  // Check if we already know it is a normalized typename or a registered
1659  // typedef (i.e. known to gROOT).
1660  if (gInterpreterHelper->ExistingTypeCheck(tname, result))
1661  {
1662  if (result.empty()) return tname;
1663  else return result;
1664  }
1665 
1666  unsigned int len = strlen(tname);
1667 
1668  unsigned int cursor = 0;
1669  bool modified = false;
1670  ResolveTypedefImpl(tname,len,cursor,modified,result);
1671 
1672  if (!modified) return tname;
1673  else return result;
1674 }
1675 
1676 
1677 ////////////////////////////////////////////////////////////////////////////////
1678 
1679 string TClassEdit::InsertStd(const char *tname)
1680 {
1681  // Return the name of type 'tname' with all STL classes prepended by "std::".
1682  // For example for "vector<set<auto_ptr<int*> > >" it returns
1683  // "std::vector<std::set<std::auto_ptr<int*> > >"
1684  //
1685 
1686  static const char* sSTLtypes[] = {
1687  "allocator",
1688  "auto_ptr",
1689  "bad_alloc",
1690  "bad_cast",
1691  "bad_exception",
1692  "bad_typeid",
1693  "basic_filebuf",
1694  "basic_fstream",
1695  "basic_ifstream",
1696  "basic_ios",
1697  "basic_iostream",
1698  "basic_istream",
1699  "basic_istringstream",
1700  "basic_ofstream",
1701  "basic_ostream",
1702  "basic_ostringstream",
1703  "basic_streambuf",
1704  "basic_string",
1705  "basic_stringbuf",
1706  "basic_stringstream",
1707  "binary_function",
1708  "binary_negate",
1709  "bitset",
1710  "char_traits",
1711  "codecvt_byname",
1712  "codecvt",
1713  "collate",
1714  "collate_byname",
1715  "compare",
1716  "complex",
1717  "ctype_byname",
1718  "ctype",
1719  "deque",
1720  "divides",
1721  "domain_error",
1722  "equal_to",
1723  "exception",
1724  "forward_list",
1725  "fpos",
1726  "greater_equal",
1727  "greater",
1728  "gslice_array",
1729  "gslice",
1730  "hash",
1731  "indirect_array",
1732  "invalid_argument",
1733  "ios_base",
1734  "istream_iterator",
1735  "istreambuf_iterator",
1736  "istrstream",
1737  "iterator_traits",
1738  "iterator",
1739  "length_error",
1740  "less_equal",
1741  "less",
1742  "list",
1743  "locale",
1744  "localedef utility",
1745  "locale utility",
1746  "logic_error",
1747  "logical_and",
1748  "logical_not",
1749  "logical_or",
1750  "map",
1751  "mask_array",
1752  "mem_fun",
1753  "mem_fun_ref",
1754  "messages",
1755  "messages_byname",
1756  "minus",
1757  "modulus",
1758  "money_get",
1759  "money_put",
1760  "moneypunct",
1761  "moneypunct_byname",
1762  "multimap",
1763  "multiplies",
1764  "multiset",
1765  "negate",
1766  "not_equal_to",
1767  "num_get",
1768  "num_put",
1769  "numeric_limits",
1770  "numpunct",
1771  "numpunct_byname",
1772  "ostream_iterator",
1773  "ostreambuf_iterator",
1774  "ostrstream",
1775  "out_of_range",
1776  "overflow_error",
1777  "pair",
1778  "plus",
1779  "pointer_to_binary_function",
1780  "pointer_to_unary_function",
1781  "priority_queue",
1782  "queue",
1783  "range_error",
1784  "raw_storage_iterator",
1785  "reverse_iterator",
1786  "runtime_error",
1787  "set",
1788  "slice_array",
1789  "slice",
1790  "stack",
1791  "string",
1792  "strstream",
1793  "strstreambuf",
1794  "time_get_byname",
1795  "time_get",
1796  "time_put_byname",
1797  "time_put",
1798  "unary_function",
1799  "unary_negate",
1800  "underflow_error",
1801  "unordered_map",
1802  "unordered_multimap",
1803  "unordered_multiset",
1804  "unordered_set",
1805  "valarray",
1806  "vector",
1807  "wstring"
1808  };
1809  static set<string> sSetSTLtypes;
1810 
1811  if (tname==0 || tname[0]==0) return "";
1812 
1813  if (sSetSTLtypes.empty()) {
1814  // set up static set
1815  const size_t nSTLtypes = sizeof(sSTLtypes) / sizeof(const char*);
1816  for (size_t i = 0; i < nSTLtypes; ++i)
1817  sSetSTLtypes.insert(sSTLtypes[i]);
1818  }
1819 
1820  size_t b = 0;
1821  size_t len = strlen(tname);
1822  string ret;
1823  ret.reserve(len + 20); // expect up to 4 extra "std::" to insert
1824  string id;
1825  while (b < len) {
1826  // find beginning of next identifier
1827  bool precScope = false; // whether the identifier was preceded by "::"
1828  while (!(isalnum(tname[b]) || tname[b] == '_') && b < len) {
1829  precScope = (b < len - 2) && (tname[b] == ':') && (tname[b + 1] == ':');
1830  if (precScope) {
1831  ret += "::";
1832  b += 2;
1833  } else
1834  ret += tname[b++];
1835  }
1836 
1837  // now b is at the beginning of an identifier or len
1838  size_t e = b;
1839  // find end of identifier
1840  id.clear();
1841  while (e < len && (isalnum(tname[e]) || tname[e] == '_'))
1842  id += tname[e++];
1843  if (!id.empty()) {
1844  if (!precScope) {
1845  set<string>::const_iterator iSTLtype = sSetSTLtypes.find(id);
1846  if (iSTLtype != sSetSTLtypes.end())
1847  ret += "std::";
1848  }
1849 
1850  ret += id;
1851  b = e;
1852  }
1853  }
1854  return ret;
1855 }
1856 
1857 ////////////////////////////////////////////////////////////////////////////////
1858 /// Demangle in a portable way the type id name.
1859 /// IMPORTANT: The caller is responsible for freeing the returned const char*
1860 
1861 char* TClassEdit::DemangleTypeIdName(const std::type_info& ti, int& errorCode)
1862 {
1863  const char* mangled_name = ti.name();
1864  return DemangleName(mangled_name, errorCode);
1865 }
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:27
ESTLType
Definition: ESTLType.h:28
ROOT::ESTLType IsSTLCont(std::string_view type)
type : type name: vector,allocator> result: 0 : not stl container code of cont...
int IsSTLCont(int testAlloc=0) const
type : type name: vector,allocator> testAlloc: if true, we test allocator...
Definition: TClassEdit.cxx:153
ClassImp(TSeqCollection) Int_t TSeqCollection TIter next(this)
Return index of object in collection.
TSplitType(const char *type2split, EModType mode=TClassEdit::kNone)
default constructor
Definition: TClassEdit.cxx:126
int GetSplit(const char *type, std::vector< std::string > &output, int &nestedLoc, EModType mode=TClassEdit::kNone)
Stores in output (after emptying it) the splited type.
Definition: TClassEdit.cxx:937
ROOT::ESTLType IsInSTL() const
type : type name: vector,allocator>[::iterator] result: 0 : not stl container ...
Definition: TClassEdit.cxx:136
#define assert(cond)
Definition: unittest.h:542
bool IsDefAlloc(const char *alloc, const char *classname)
return whether or not 'allocname' is the STL default allocator for type 'classname' ...
Definition: TClassEdit.cxx:556
int compare(double v1, double v2, const std::string &name="", double scale=1.0)
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 ...
std::string InsertStd(const char *tname)
TArc * a
Definition: textangle.C:12
static void RemoveStd(std::string &name, size_t pos=0)
Remove std:: and any potential inline namespace (well compiler detail namespace.
Definition: TClassEdit.cxx:74
bool IsSTLBitset(const char *type)
Return true is the name is std::bitset or bitset
STL namespace.
int STLArgs(int kind)
Return number of arguments for STL container before allocator.
Definition: TClassEdit.cxx:511
void ShortType(std::string &answer, int mode)
Return the absolute type of typeDesc into the string answ.
Definition: TClassEdit.cxx:197
char * DemangleName(const char *mangled_name, int &errorCode)
Definition: TClassEdit.h:178
void Init(TClassEdit::TInterpreterLookupHelper *helper)
Definition: TClassEdit.cxx:118
std::string ResolveTypedef(const char *tname, bool resolveAll=false)
ROOT::ESTLType STLKind(std::string_view type)
Converts STL container name to number.
Definition: TClassEdit.cxx:467
static size_t findNameEnd(const std::string_view full)
Definition: TClassEdit.cxx:524
char * DemangleTypeIdName(const std::type_info &ti, int &errorCode)
Demangle in a portable way the type id name.
XFontStruct * id
Definition: TGX11.cxx:108
bool IsInterpreterDetail(const char *type)
Return true if the type is one the interpreter details which are only forward declared (ClassInfo_t e...
std::string GetLong64_Name(const char *original)
Replace 'long long' and 'unsigned long long' by 'Long64_t' and 'ULong64_t'.
Definition: TClassEdit.cxx:816
ROOT::ESTLType UnderlyingIsSTLCont(std::string_view type)
Return the type of STL collection, if any, that is the underlying type of the given type...
EComplexType GetComplexType(const char *)
Definition: TClassEdit.cxx:96
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)
bool IsDefHash(const char *hashname, const char *classname)
return whether or not 'hashname' is the STL default hash for type 'classname'
Definition: TClassEdit.cxx:772
basic_string_view< char > string_view
static void R__FindTrailing(std::string &full, std::string &stars)
Definition: TClassEdit.cxx:870
std::vector< std::string > fElements
Definition: TClassEdit.h:139
return
Definition: TBase64.cxx:62
SVector< double, 2 > v
Definition: Dict.h:5
void GetNormalizedName(std::string &norm_name, std::string_view name)
Return the normalized name.
Definition: TClassEdit.cxx:787
bool IsVectorBool(const char *name)
const Handle_t kNone
Definition: GuiTypes.h:89
bool IsStdClass(const char *type)
return true if the class belongs to the std namespace
int type
Definition: TGX11.cxx:120
bool IsDefComp(const char *comp, const char *classname)
return whether or not 'compare' is the STL default comparator for type 'classname' ...
Definition: TClassEdit.cxx:754
#define name(a, b)
Definition: linkTestLib0.cpp:5
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:712
const char * GetUnqualifiedName(const char *name)
Return the start of the unqualified name include in 'original'.
Definition: TClassEdit.cxx:850
std::string ShortType(const char *typeDesc, int mode)
Return the absolute type of typeDesc.
double result[121]
static void output(int code)
Definition: gifencode.c:226
const Int_t n
Definition: legend1.C:16
static void ResolveTypedefImpl(const char *tname, unsigned int len, unsigned int &cursor, bool &modified, std::string &result)
bool IsDefPred(const char *predname, const char *classname)
return whether or not 'predname' is the STL default predicate for type 'classname' ...
Definition: TClassEdit.cxx:763