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