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