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