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