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