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