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