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