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 (std::isspace(c[0])) {
1308 if (kbl) continue;
1309 if (!isalnum(c[ 1]) && c[ 1] !='_') continue;
1310 if (c[0] != ' ') {
1311 // Significant whitespace other than ' ' (e.g. \n in a multi-line
1312 // class name from a selection XML) is replaced by a plain space.
1313 result += ' ';
1314 kbl = 1;
1315 continue;
1316 }
1317 }
1318 if (kbl && (mode>=2 || parensStack.empty())) { //remove "const' etc...
1319 int done = 0;
1320 size_t n = (mode) ? std::size(remove) : 1;
1321
1322 // loop on all the keywords we want to remove
1323 for (size_t k = 0; k < n; k++) {
1324 auto rlen = lengths[k];
1325
1326 // Do we have a match
1327 if (strncmp(remove[k],c,rlen)) continue;
1328
1329 // make sure that the 'keyword' is not part of a longer indentifier
1330 if (isalnum(c[rlen]) || c[rlen]=='_' || c[rlen]=='$') continue;
1331
1332 c+=rlen-1; done = 1; break;
1333 }
1334 if (done) continue;
1335 }
1336
1337 kbl = (!isalnum(c[ 0]) && c[ 0]!='_' && c[ 0]!='$' && c[0]!='[' && c[0]!=']' && c[0]!='-' && c[0]!='@');
1338 // '@' is special character used only the artifical class name used by ROOT to implement the
1339 // I/O customization rules that requires caching of the input data.
1340
1341 if (*c == '<' || *c == '(')
1342 parensStack.push_back(*c);
1343
1344 if (parensStack.empty() && !isalnum(*c)) {
1345 if (!strchr("*&:._$ []-@",*c)) break;
1346 // '.' is used as a module/namespace separator by PyROOT, see
1347 // TPyClassGenerator::GetClass.
1348 }
1349 if (c[0]=='>' && result.size() && result[result.size()-1]=='>') result+=" ";
1350
1351 result += c[0];
1352
1353 if (*c == '>' && !parensStack.empty() && parensStack.back() == '<')
1354 parensStack.pop_back();
1355 else if (*c == ')' && !parensStack.empty() && parensStack.back() == '(')
1356 parensStack.pop_back();
1357 }
1358 if(tail) *tail=c;
1359 return result;
1360}
1361
1362////////////////////////////////////////////////////////////////////////////////
1363//////////////////////////////////////////////////////////////////////////////
1364/// Return the absolute type of typeDesc.
1365/// E.g.: typeDesc = "class const volatile TNamed**", returns "TNamed**".
1366/// if (mode&1) remove last "*"s returns "TNamed"
1367/// if (mode&2) remove default allocators from STL containers
1368/// if (mode&4) remove all allocators from STL containers
1369/// if (mode&8) return inner class of stl container. list<innerClass>
1370/// if (mode&16) return deapest class of stl container. vector<list<deapest>>
1371/// if (mode&kDropAllDefault) remove default template arguments
1372//////////////////////////////////////////////////////////////////////////////
1373
1374string TClassEdit::ShortType(const char *typeDesc, int mode)
1375{
1376 string answer;
1377
1378 // get list of all arguments
1379 if (typeDesc) {
1381 arglist.ShortType(answer, mode);
1382 }
1383
1384 return answer;
1385}
1386
1387////////////////////////////////////////////////////////////////////////////////
1388/// Return true if the type is one the interpreter details which are
1389/// only forward declared (ClassInfo_t etc..)
1390
1392{
1393 size_t len = strlen(type);
1394 if (len < 2 || strncmp(type+len-2,"_t",2) != 0) return false;
1395
1396 unsigned char offset = 0;
1397 if (strncmp(type,"const ",6)==0) { offset += 6; }
1398 static const char *names[] = { "CallFunc_t","ClassInfo_t","BaseClassInfo_t",
1399 "DataMemberInfo_t","FuncTempInfo_t","MethodInfo_t","MethodArgInfo_t",
1400 "TypeInfo_t", "TypedefInfo_t", nullptr};
1401
1402 for(int k=1;names[k];k++) {if (strcmp(type+offset,names[k])==0) return true;}
1403 return false;
1404}
1405
1406////////////////////////////////////////////////////////////////////////////////
1407/// Return true is the name is std::bitset<number> or bitset<number>
1408
1409bool TClassEdit::IsSTLBitset(const char *classname)
1410{
1411 size_t offset = StdLen(classname);
1412 if ( strncmp(classname+offset,"bitset<",std::char_traits<char>::length("bitset<"))==0) return true;
1413 return false;
1414}
1415
1416////////////////////////////////////////////////////////////////////////////////
1417/// Return the type of STL collection, if any, that is the underlying type
1418/// of the given type. Namely return the value of IsSTLCont after stripping
1419/// pointer, reference and constness from the type.
1420/// UnderlyingIsSTLCont("vector<int>*") == IsSTLCont("vector<int>")
1421/// See TClassEdit::IsSTLCont
1422///
1423/// type : type name: vector<list<classA,allocator>,allocator>*
1424/// result: 0 : not stl container
1425/// code of container 1=vector,2=list,3=deque,4=map
1426/// 5=multimap,6=set,7=multiset
1427
1429{
1430 if (type.compare(0,6,"const ",6) == 0)
1431 type.remove_prefix(6);
1432
1433 while(type[type.length()-1]=='*' ||
1434 type[type.length()-1]=='&' ||
1435 type[type.length()-1]==' ') {
1436 type.remove_suffix(1);
1437 }
1438 return IsSTLCont(type);
1439}
1440
1441////////////////////////////////////////////////////////////////////////////////
1442/// type : type name: vector<list<classA,allocator>,allocator>
1443/// result: 0 : not stl container
1444/// code of container 1=vector,2=list,3=deque,4=map
1445/// 5=multimap,6=set,7=multiset
1446
1448{
1449 auto pos = type.find('<');
1450 if (pos==std::string_view::npos) return ROOT::kNotSTL;
1451
1452 auto c = pos+1;
1453 for (decltype(type.length()) level = 1; c < type.length(); ++c) {
1454 if (type[c] == '<') ++level;
1455 if (type[c] == '>') --level;
1456 if (level == 0) break;
1457 }
1458 if (c != (type.length()-1) ) {
1459 return ROOT::kNotSTL;
1460 }
1461
1462 return STLKind(type.substr(0,pos));
1463}
1464
1465////////////////////////////////////////////////////////////////////////////////
1466/// type : type name: vector<list<classA,allocator>,allocator>
1467/// testAlloc: if true, we test allocator, if it is not default result is negative
1468/// result: 0 : not stl container
1469/// abs(result): code of container 1=vector,2=list,3=deque,4=map
1470/// 5=multimap,6=set,7=multiset
1471/// positive val: we have a vector or list with default allocator to any depth
1472/// like vector<list<vector<int>>>
1473/// negative val: STL container other than vector or list, or non default allocator
1474/// For example: vector<deque<int>> has answer -1
1475
1477{
1478 if (!strchr(type,'<')) return 0;
1479
1481 return arglist.IsSTLCont(testAlloc);
1482}
1483
1484////////////////////////////////////////////////////////////////////////////////
1485/// return true if the class belongs to the std namespace
1486
1487bool TClassEdit::IsStdClass(const char *classname)
1488{
1489 constexpr auto length = std::char_traits<char>::length;
1490 classname += StdLen( classname );
1491 if ( strcmp(classname,"string")==0 ) return true;
1492 if ( strncmp(classname,"bitset<",length("bitset<"))==0) return true;
1493 if ( IsStdPair(classname) ) return true;
1494 if ( strcmp(classname,"allocator")==0) return true;
1495 if ( strncmp(classname,"allocator<",length("allocator<"))==0) return true;
1496 if ( strncmp(classname,"greater<",length("greater<"))==0) return true;
1497 if ( strncmp(classname,"less<",length("less<"))==0) return true;
1498 if ( strncmp(classname,"equal_to<",length("equal_to<"))==0) return true;
1499 if ( strncmp(classname,"hash<",length("hash<"))==0) return true;
1500 if ( strncmp(classname,"auto_ptr<",length("auto_ptr<"))==0) return true;
1501
1502 if ( strncmp(classname,"vector<",length("vector<"))==0) return true;
1503 if ( strncmp(classname,"list<",length("list<"))==0) return true;
1504 if ( strncmp(classname,"forward_list<",length("forward_list<"))==0) return true;
1505 if ( strncmp(classname,"deque<",length("deque<"))==0) return true;
1506 if ( strncmp(classname,"map<",length("map<"))==0) return true;
1507 if ( strncmp(classname,"multimap<",length("multimap<"))==0) return true;
1508 if ( strncmp(classname,"set<",length("set<"))==0) return true;
1509 if ( strncmp(classname,"multiset<",length("multiset<"))==0) return true;
1510 if ( strncmp(classname,"unordered_set<",length("unordered_set<"))==0) return true;
1511 if ( strncmp(classname,"unordered_multiset<",length("unordered_multiset<"))==0) return true;
1512 if ( strncmp(classname,"unordered_map<",length("unordered_map<"))==0) return true;
1513 if ( strncmp(classname,"unordered_multimap<",length("unordered_multimap<"))==0) return true;
1514 if ( strncmp(classname,"bitset<",length("bitset<"))==0) return true;
1515 if ( strncmp(classname,"ROOT::VecOps::RVec<",length("ROOT::VecOps::RVec<"))==0) return true;
1516
1517 return false;
1518}
1519
1520
1521////////////////////////////////////////////////////////////////////////////////
1522
1525
1526 return ( TClassEdit::STLKind( splitname.fElements[0] ) == ROOT::kSTLvector)
1527 && ( splitname.fElements[1] == "bool" || splitname.fElements[1]=="Bool_t");
1528}
1529
1530////////////////////////////////////////////////////////////////////////////////
1531
1532static void ResolveTypedefProcessType(const char *tname,
1533 unsigned int /* len */,
1534 unsigned int cursor,
1535 bool constprefix,
1536 unsigned int start_of_type,
1537 unsigned int end_of_type,
1538 unsigned int mod_start_of_type,
1539 bool &modified,
1540 std::string &result)
1541{
1542 std::string type(modified && (mod_start_of_type < result.length()) ?
1543 result.substr(mod_start_of_type, string::npos)
1544 : 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
1545 string typeresult;
1546 if (gInterpreterHelper->ExistingTypeCheck(type, typeresult)
1547 || gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(type, typeresult, false)) {
1548 // it is a known type
1549 if (!typeresult.empty()) {
1550 // and it is a typedef, we need to replace it in the output.
1551 if (modified) {
1552 result.replace(mod_start_of_type, string::npos,
1553 typeresult);
1554 }
1555 else {
1556 modified = true;
1557 result += string(tname,0,start_of_type);
1558 if (constprefix && typeresult.compare(0,6,"const ",6) == 0) {
1559 result += typeresult.substr(6,string::npos);
1560 } else {
1561 result += typeresult;
1562 }
1563 }
1564 } else if (modified) {
1565 result.replace(mod_start_of_type, string::npos,
1566 type);
1567 }
1568 if (modified) {
1569 if (end_of_type != 0 && end_of_type!=cursor) {
1570 result += std::string(tname,end_of_type,cursor-end_of_type);
1571 }
1572 }
1573 } else {
1574 // no change needed.
1575 if (modified) {
1576 // result += type;
1577 if (end_of_type != 0 && end_of_type!=cursor) {
1578 result += std::string(tname,end_of_type,cursor-end_of_type);
1579 }
1580 }
1581 }
1582}
1583
1584////////////////////////////////////////////////////////////////////////////////
1585
1586static void ResolveTypedefImpl(const char *tname,
1587 unsigned int len,
1588 unsigned int &cursor,
1589 bool &modified,
1590 std::string &result)
1591{
1592 // Need to parse and deal with
1593 // A::B::C< D, E::F, G::H<I,J>::K::L >::M
1594 // where E might be replace by N<O,P>
1595 // and G::H<I,J>::K or G might be a typedef.
1596
1597 bool constprefix = false;
1598
1599 if (tname[cursor]==' ') {
1600 if (!modified) {
1601 modified = true;
1602 result += string(tname,0,cursor);
1603 }
1604 while (tname[cursor]==' ') ++cursor;
1605 }
1606 // In Windows, we might have 'class const ...' as name,
1607 // (never 'const class ...'), so skip the leading 'class ', if any
1608 if (strncmp(tname+cursor,"class ",6) == 0) {
1609 cursor += 6;
1610 }
1611 if (strncmp(tname+cursor,"const ",6) == 0) {
1612 cursor += 6;
1613 if (modified) result += "const ";
1614 constprefix = true;
1615 }
1616
1617 if (len > 2 && strncmp(tname+cursor,"::",2) == 0) {
1618 cursor += 2;
1619 }
1620
1621 unsigned int start_of_type = cursor;
1622 unsigned int end_of_type = 0;
1623 unsigned int mod_start_of_type = result.length();
1624 unsigned int prevScope = cursor;
1625 for ( ; cursor<len; ++cursor) {
1626 switch (tname[cursor]) {
1627 case ':': {
1628 if ((cursor+1)>=len || tname[cursor+1]!=':') {
1629 // we expected another ':', malformed, give up.
1630 if (modified) result += (tname+prevScope);
1631 return;
1632 }
1633 string scope;
1634 if (modified) {
1635 scope = result.substr(mod_start_of_type, string::npos);
1636 scope += std::string(tname+prevScope,cursor-prevScope);
1637 } else {
1638 scope = std::string(tname, start_of_type, cursor - start_of_type); // we need to try to avoid this copy
1639 }
1640 std::string scoperesult;
1641 bool isInlined = false;
1642 if (gInterpreterHelper->ExistingTypeCheck(scope, scoperesult)
1643 ||gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(scope, scoperesult)) {
1644 // it is a known type
1645 if (!scoperesult.empty()) {
1646 // and it is a typedef
1647 if (modified) {
1648 if (constprefix && scoperesult.compare(0,6,"const ",6) != 0) mod_start_of_type -= 6;
1649 result.replace(mod_start_of_type, string::npos,
1650 scoperesult);
1651 result += "::";
1652 } else {
1653 modified = true;
1655 result += string(tname,0,start_of_type);
1656 //if (constprefix) result += "const ";
1658 result += "::";
1659 }
1660 } else if (modified) {
1661 result += std::string(tname+prevScope,cursor+2-prevScope);
1662 }
1663 } else if (!gInterpreterHelper->IsDeclaredScope(scope,isInlined)) {
1664 // the nesting namespace is not declared, just ignore it and move on
1665 if (modified) result += std::string(tname+prevScope,cursor+2-prevScope);
1666 } else if (isInlined) {
1667 // humm ... just skip it.
1668 if (!modified) {
1669 modified = true;
1671 result += string(tname,0,start_of_type);
1672 //if (constprefix) result += "const ";
1674 }
1675 } else if (modified) {
1676 result += std::string(tname+prevScope,cursor+2-prevScope);
1677 }
1678 // Consume the 1st semi colon, the 2nd will be consume by the for loop.
1679 ++cursor;
1680 prevScope = cursor+1;
1681 break;
1682 }
1683 case '(':
1684 case '<': {
1685 // We move on in presence of function pointers, i.e. if we find '(*)' (with spaces which could be in
1686 // between), for example cases like:
1687 // pair<dd4hep::sim::Geant4Sensitive*,Geant4HitCollection*(*)(const std::string&,const std::string&,Geant4Sensitive*)>
1688 // This honors #18842 without breaking #18833.
1689 auto nStars = 0u;
1690 auto next = cursor + 1;
1691 for (; next != cursor && nStars < 2 && next < len; next++) {
1692 if (' ' == tname[next]) {
1693 // We simply skip spaces
1694 continue;
1695 } else if ('*' == tname[next]) {
1696 nStars++;
1697 } else if (')' == tname[next]) {
1698 if (nStars == 1) {
1699 cursor = next;
1700 } else {
1701 break;
1702 }
1703 } else {
1704 // if the token is not ' ', '*', or ')' we move on
1705 break;
1706 }
1707 }
1708 // If we found '(*)' (with potentially spaces in between the characters)
1709 // we move on with the parsing
1710 if (cursor == next)
1711 break;
1712
1713 // push information on stack
1714 if (modified) {
1715 result += std::string(tname+prevScope,cursor+1-prevScope);
1716 // above includes the '<' .... result += '<';
1717 }
1718 do {
1719 ++cursor;
1721 } while( cursor<len && tname[cursor] == ',' );
1722
1723 while (cursor<len && tname[cursor+1]==' ') ++cursor;
1724
1725 // Since we already checked the type, skip the next section
1726 // (respective the scope section and final type processing section)
1727 // as they would re-do the same job.
1728 if (cursor+2<len && tname[cursor+1]==':' && tname[cursor+2]==':') {
1729 if (modified) result += "::";
1730 cursor += 2;
1731 prevScope = cursor+1;
1732 }
1733 if ( (cursor+1)<len && tname[cursor+1] == ',') {
1734 ++cursor;
1735 if (modified) result += ',';
1736 return;
1737 }
1738 if ( (cursor+1)<len && tname[cursor+1] == '>') {
1739 ++cursor;
1740 if (modified) result += " >";
1741 return;
1742 }
1743 if ( (cursor+1)<len && tname[cursor+1] == ')') {
1744 ++cursor;
1745 if (modified) result += ")";
1746 return;
1747 }
1748 if ( (cursor+1) >= len) {
1749 return;
1750 }
1751 if (tname[cursor] != ' ') break;
1752 if (modified) prevScope = cursor+1;
1753 // If the 'current' character is a space we need to treat it,
1754 // since this the next case statement, we can just fall through,
1755 // otherwise we should need to do:
1756 // --cursor; break;
1757 }
1758 case ' ': {
1760 // let's see if we have 'long long' or 'unsigned int' or 'signed char' or what not.
1761 while ((cursor+1)<len && tname[cursor+1] == ' ') ++cursor;
1762
1763 auto next = cursor+1;
1764 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] == ']'))
1765 {
1766 // A first const after the type needs to be move in the front.
1767 if (!modified) {
1768 modified = true;
1769 result += string(tname,0,start_of_type);
1770 result += "const ";
1773 } else if (mod_start_of_type < result.length()) {
1774 result.insert(mod_start_of_type,"const ");
1775 mod_start_of_type += 6;
1776 } else {
1777 result += "const ";
1778 mod_start_of_type += 6;
1780 }
1781 cursor += 5;
1782 end_of_type = cursor+1;
1784 if ((next+5)==len || tname[next+5] == ',' || tname[next+5] == '>' || tname[next+5] == ')' || tname[next+5] == '[') {
1785 break;
1786 }
1787 } else if (next!=len && tname[next] != '*' && tname[next] != '&') {
1788 // the type is not ended yet.
1789 end_of_type = 0;
1790 break;
1791 }
1792 ++cursor;
1793 // Intentional fall through;
1794 }
1795 case '*':
1796 case '&': {
1797 if (tname[cursor] != ' ') end_of_type = cursor;
1798 // check and skip const (followed by *,&, ,) ... what about followed by ':','['?
1799 auto next = cursor+1;
1800 if (strncmp(tname+next,"const",5) == 0) {
1801 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] == '[') {
1802 next += 5;
1803 }
1804 }
1805 while (next<len &&
1806 (tname[next] == ' ' || tname[next] == '*' || tname[next] == '&')) {
1807 ++next;
1808 // check and skip const (followed by *,&, ,) ... what about followed by ':','['?
1809 if (strncmp(tname+next,"const",5) == 0) {
1810 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] == '[') {
1811 next += 5;
1812 }
1813 }
1814 }
1815 cursor = next-1;
1816// if (modified && mod_start_of_type < result.length()) {
1817// result += string(tname,end_of_type,cursor-end_of_type);
1818// }
1819 break;
1820 }
1821 case ',': {
1822 if (modified && prevScope) {
1823 result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1824 }
1826 modified, result);
1827 if (modified) result += ',';
1828 return;
1829 }
1830 case ')':
1831 case '>': {
1832 char c = tname[cursor];
1833 if (modified && prevScope) {
1834 result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1835 }
1837 modified, result);
1838 if (modified) result += c;
1839 return;
1840 }
1841 default:
1842 end_of_type = 0;
1843 }
1844 }
1845
1846 if (prevScope && modified) result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1847
1849 modified, result);
1850}
1851
1852
1853////////////////////////////////////////////////////////////////////////////////
1854
1855string TClassEdit::ResolveTypedef(const char *tname, bool /* resolveAll */)
1856{
1857 // Return the name of type 'tname' with all its typedef components replaced
1858 // by the actual type its points to
1859 // For example for "typedef MyObj MyObjTypedef;"
1860 // vector<MyObjTypedef> return vector<MyObj>
1861 //
1862
1863 if (!tname || *tname == 0)
1864 return "";
1865 if (!gInterpreterHelper)
1866 return tname;
1867
1868 std::string result;
1869
1870 // Check if we already know it is a normalized typename or a registered
1871 // typedef (i.e. known to gROOT).
1872 if (gInterpreterHelper->ExistingTypeCheck(tname, result))
1873 {
1874 if (result.empty()) return tname;
1875 else return result;
1876 }
1877
1878 unsigned int len = strlen(tname);
1879
1880 unsigned int cursor = 0;
1881 bool modified = false;
1883
1884 if (!modified) return tname;
1885 else return result;
1886}
1887
1888
1889////////////////////////////////////////////////////////////////////////////////
1890
1891string TClassEdit::InsertStd(const char *tname)
1892{
1893 // Return the name of type 'tname' with all STL classes prepended by "std::".
1894 // For example for "vector<set<auto_ptr<int*> > >" it returns
1895 // "std::vector<std::set<std::auto_ptr<int*> > >"
1896 //
1897
1898 static const char* sSTLtypes[] = {
1899 "allocator",
1900 "auto_ptr",
1901 "bad_alloc",
1902 "bad_cast",
1903 "bad_exception",
1904 "bad_typeid",
1905 "basic_filebuf",
1906 "basic_fstream",
1907 "basic_ifstream",
1908 "basic_ios",
1909 "basic_iostream",
1910 "basic_istream",
1911 "basic_istringstream",
1912 "basic_ofstream",
1913 "basic_ostream",
1914 "basic_ostringstream",
1915 "basic_streambuf",
1916 "basic_string",
1917 "basic_stringbuf",
1918 "basic_stringstream",
1919 "binary_function",
1920 "binary_negate",
1921 "bitset",
1922 "char_traits",
1923 "codecvt_byname",
1924 "codecvt",
1925 "collate",
1926 "collate_byname",
1927 "compare",
1928 "complex",
1929 "ctype_byname",
1930 "ctype",
1931 "deque",
1932 "divides",
1933 "domain_error",
1934 "equal_to",
1935 "exception",
1936 "forward_list",
1937 "fpos",
1938 "greater_equal",
1939 "greater",
1940 "gslice_array",
1941 "gslice",
1942 "hash",
1943 "indirect_array",
1944 "invalid_argument",
1945 "ios_base",
1946 "istream_iterator",
1947 "istreambuf_iterator",
1948 "istrstream",
1949 "iterator_traits",
1950 "iterator",
1951 "length_error",
1952 "less_equal",
1953 "less",
1954 "list",
1955 "locale",
1956 "localedef utility",
1957 "locale utility",
1958 "logic_error",
1959 "logical_and",
1960 "logical_not",
1961 "logical_or",
1962 "map",
1963 "mask_array",
1964 "mem_fun",
1965 "mem_fun_ref",
1966 "messages",
1967 "messages_byname",
1968 "minus",
1969 "modulus",
1970 "money_get",
1971 "money_put",
1972 "moneypunct",
1973 "moneypunct_byname",
1974 "multimap",
1975 "multiplies",
1976 "multiset",
1977 "negate",
1978 "not_equal_to",
1979 "num_get",
1980 "num_put",
1981 "numeric_limits",
1982 "numpunct",
1983 "numpunct_byname",
1984 "ostream_iterator",
1985 "ostreambuf_iterator",
1986 "ostrstream",
1987 "out_of_range",
1988 "overflow_error",
1989 "pair",
1990 "plus",
1991 "pointer_to_binary_function",
1992 "pointer_to_unary_function",
1993 "priority_queue",
1994 "queue",
1995 "range_error",
1996 "raw_storage_iterator",
1997 "reverse_iterator",
1998 "runtime_error",
1999 "set",
2000 "slice_array",
2001 "slice",
2002 "stack",
2003 "string",
2004 "strstream",
2005 "strstreambuf",
2006 "time_get_byname",
2007 "time_get",
2008 "time_put_byname",
2009 "time_put",
2010 "unary_function",
2011 "unary_negate",
2012 "unique_pointer",
2013 "underflow_error",
2014 "unordered_map",
2015 "unordered_multimap",
2016 "unordered_multiset",
2017 "unordered_set",
2018 "valarray",
2019 "vector",
2020 "wstring"
2021 };
2022
2023 if (!tname || *tname == 0) return "";
2024
2025 auto initSetSTLtypes = []() {
2026 std::set<std::string> iSetSTLtypes;
2027 // set up static set
2028 const size_t nSTLtypes = sizeof(sSTLtypes) / sizeof(const char*);
2029 for (size_t i = 0; i < nSTLtypes; ++i)
2030 iSetSTLtypes.insert(sSTLtypes[i]);
2031 return iSetSTLtypes;
2032 };
2034
2035 size_t b = 0;
2036 size_t len = strlen(tname);
2037 string ret;
2038 ret.reserve(len + 20); // expect up to 4 extra "std::" to insert
2039 string id;
2040 while (b < len) {
2041 // find beginning of next identifier
2042 bool precScope = false; // whether the identifier was preceded by "::"
2043 while (!(isalnum(tname[b]) || tname[b] == '_') && b < len) {
2044 precScope = (b < len - 2) && (tname[b] == ':') && (tname[b + 1] == ':');
2045 if (precScope) {
2046 ret += "::";
2047 b += 2;
2048 } else
2049 ret += tname[b++];
2050 }
2051
2052 // now b is at the beginning of an identifier or len
2053 size_t e = b;
2054 // find end of identifier
2055 id.clear();
2056 while (e < len && (isalnum(tname[e]) || tname[e] == '_'))
2057 id += tname[e++];
2058 if (!id.empty()) {
2059 if (!precScope) {
2060 set<string>::const_iterator iSTLtype = sSetSTLtypes.find(id);
2061 if (iSTLtype != sSetSTLtypes.end())
2062 ret += "std::";
2063 }
2064
2065 ret += id;
2066 b = e;
2067 }
2068 }
2069 return ret;
2070}
2071
2072////////////////////////////////////////////////////////////////////////////////
2073/// An helper class to dismount the name and remount it changed whenever
2074/// necessary
2075
2077 std::string fName;
2078 std::vector<std::unique_ptr<NameCleanerForIO>> fArgumentNodes = {};
2080 bool fHasChanged = false;
2082 {
2083 NameCleanerForIO* mother = fMother;
2084 if (!mother) return false;
2085 bool isSTLContOrArray = true;
2086 while (nullptr != mother){
2087 auto stlType = TClassEdit::IsSTLCont(mother->fName+"<>");
2089 mother = mother->fMother;
2090 }
2091
2092 return isSTLContOrArray;
2093 }
2094
2095public:
2096 NameCleanerForIO(const std::string& clName = "",
2098 NameCleanerForIO* mother = nullptr):fMother(mother)
2099 {
2100 if (clName.back() != '>') {
2101 fName = clName;
2102 return;
2103 }
2104
2105 std::vector<std::string> v;
2106 int dummy=0;
2107 TClassEdit::GetSplit(clName.c_str(), v, dummy, mode);
2108
2109 // We could be in presence of templates such as A1<T1>::A2<T2>::A3<T3>
2110 auto argsEnd = v.end();
2111 auto argsBeginPlusOne = ++v.begin();
2112 auto argPos = std::find_if(argsBeginPlusOne, argsEnd,
2113 [](std::string& arg){return (!arg.empty() && arg.front() == ':');});
2114 if (argPos != argsEnd) {
2115 const int length = clName.size();
2116 int wedgeBalance = 0;
2117 int lastOpenWedge = 0;
2118 for (int i=length-1;i>-1;i--) {
2119 auto& c = clName.at(i);
2120 if (c == '<') {
2121 wedgeBalance++;
2122 lastOpenWedge = i;
2123 } else if (c == '>') {
2124 wedgeBalance--;
2125 } else if (c == ':' && 0 == wedgeBalance) {
2126 // This would be A1<T1>::A2<T2>
2127 auto nameToClean = clName.substr(0,i-1);
2129 auto cleanName = node.ToString();
2130 fHasChanged = node.HasChanged();
2131 // We got A1<T1>::A2<T2> cleaned
2132
2133 // We build the changed A1<T1>::A2<T2>::A3
2134 cleanName += "::";
2135 // Now we get A3 and append it
2136 cleanName += clName.substr(i+1,lastOpenWedge-i-1);
2137
2138 // We now get the args of what in our case is A1<T1>::A2<T2>::A3
2139 auto lastTemplate = &clName.data()[i+1];
2140
2141 // We split it
2143 // We now replace the name of the template
2144 v[0] = cleanName;
2145 break;
2146 }
2147 }
2148 }
2149
2150 fName = v.front();
2151 unsigned int nargs = v.size() - 2;
2152 for (unsigned int i=0;i<nargs;++i) {
2153 fArgumentNodes.emplace_back(new NameCleanerForIO(v[i+1],mode,this));
2154 }
2155 }
2156
2157 bool HasChanged() const {return fHasChanged;}
2158
2159 std::string ToString()
2160 {
2161 std::string name(fName);
2162
2163 if (fArgumentNodes.empty()) return name;
2164
2165 // We have in hands a case like unique_ptr< ... >
2166 // Perhaps we could treat atomics as well like this?
2167 if (!fMother && TClassEdit::IsUniquePtr(fName+"<")) {
2168 name = fArgumentNodes.front()->ToString();
2169 // ROOT-9933: we remove const if present.
2171 tst.ShortType(name, 1);
2172 name += "*";
2173 fHasChanged = true;
2174 return name;
2175 }
2176
2177 // Now we treat the case of the collections of unique ptrs
2178 auto stlContType = AreAncestorsSTLContOrArray();
2179 if (stlContType != ROOT::kNotSTL && TClassEdit::IsUniquePtr(fName+"<")) {
2180 name = fArgumentNodes.front()->ToString();
2181 name += "*";
2182 fHasChanged = true;
2183 return name;
2184 }
2185
2186 name += "<";
2187 for (auto& node : fArgumentNodes) {
2188 name += node->ToString() + ",";
2189 fHasChanged |= node->HasChanged();
2190 }
2191 name.pop_back(); // Remove the last comma.
2192 name += name.back() == '>' ? " >" : ">"; // Respect name normalisation
2193 return name;
2194 }
2195
2196 const std::string& GetName() {return fName;}
2197 const std::vector<std::unique_ptr<NameCleanerForIO>>* GetChildNodes() const {return &fArgumentNodes;}
2198};
2199
2200////////////////////////////////////////////////////////////////////////////////
2201
2202std::string TClassEdit::GetNameForIO(const std::string& templateInstanceName,
2204 bool* hasChanged)
2205{
2206 // Decompose template name into pieces and remount it applying the necessary
2207 // transformations necessary for the ROOT IO subsystem, namely:
2208 // - Transform std::unique_ptr<T> into T (for selections) (also nested)
2209 // - Transform std::unique_ptr<const T> into T (for selections) (also nested)
2210 // - Transform std::COLL<std::unique_ptr<T>> into std::COLL<T*> (also nested)
2211 // Name normalisation is respected (e.g. spaces).
2212 // The implementation uses an internal class defined in the cxx file.
2214 auto nameForIO = node.ToString();
2215 if (hasChanged) {
2216 *hasChanged = node.HasChanged();
2217 }
2218 return nameForIO;
2219}
2220
2221////////////////////////////////////////////////////////////////////////////////
2222// We could introduce a tuple as return type, but we be consistent with the rest
2223// of the code.
2224bool TClassEdit::GetStdArrayProperties(const char* typeName,
2225 std::string& typeNameBuf,
2226 std::array<int, 5>& maxIndices,
2227 int& ndim)
2228{
2229 if (!IsStdArray(typeName)) return false;
2230
2231 // We have an array, it's worth continuing
2232 NameCleanerForIO node(typeName);
2233
2234 // We now recurse updating the data according to what we find
2235 auto childNodes = node.GetChildNodes();
2236 for (ndim = 1;ndim <=5 ; ndim++) {
2237 maxIndices[ndim-1] = std::atoi(childNodes->back()->GetName().c_str());
2238 auto& frontNode = childNodes->front();
2239 typeNameBuf = frontNode->GetName();
2240 if (! IsStdArray(typeNameBuf+"<")) {
2241 typeNameBuf = frontNode->ToString();
2242 return true;
2243 }
2244 childNodes = frontNode->GetChildNodes();
2245 }
2246
2247 return true;
2248}
2249
2250
2251////////////////////////////////////////////////////////////////////////////////
2252/// Demangle in a portable way the type id name.
2253/// IMPORTANT: The caller is responsible for freeing the returned const char*
2254
2255char* TClassEdit::DemangleTypeIdName(const std::type_info& ti, int& errorCode)
2256{
2257 const char* mangled_name = ti.name();
2259}
2260/*
2261/// Result of splitting a function declaration into
2262/// fReturnType fScopeName::fFunctionName<fFunctionTemplateArguments>(fFunctionParameters)
2263struct FunctionSplitInfo {
2264 /// Return type of the function, might be empty if the function declaration string did not provide it.
2265 std::string fReturnType;
2266
2267 /// Name of the scope qualification of the function, possibly empty
2268 std::string fScopeName;
2269
2270 /// Name of the function
2271 std::string fFunctionName;
2272
2273 /// Template arguments of the function template specialization, if any; will contain one element "" for
2274 /// `function<>()`
2275 std::vector<std::string> fFunctionTemplateArguments;
2276
2277 /// Function parameters.
2278 std::vector<std::string> fFunctionParameters;
2279};
2280*/
2281
2282namespace {
2283 /// Find the first occurrence of any of needle's characters in haystack that
2284 /// is not nested in a <>, () or [] pair.
2285 std::size_t FindNonNestedNeedles(std::string_view haystack, string_view needles)
2286 {
2287 std::stack<char> expected;
2288 for (std::size_t pos = 0, end = haystack.length(); pos < end; ++pos) {
2289 char c = haystack[pos];
2290 if (expected.empty()) {
2291 if (needles.find(c) != std::string_view::npos)
2292 return pos;
2293 } else {
2294 if (c == expected.top()) {
2295 expected.pop();
2296 continue;
2297 }
2298 }
2299 switch (c) {
2300 case '<': expected.emplace('>'); break;
2301 case '(': expected.emplace(')'); break;
2302 case '[': expected.emplace(']'); break;
2303 }
2304 }
2305 return std::string_view::npos;
2306 }
2307
2308 /// Find the first occurrence of `::` that is not nested in a <>, () or [] pair.
2309 std::size_t FindNonNestedDoubleColons(std::string_view haystack)
2310 {
2311 std::size_t lenHaystack = haystack.length();
2312 std::size_t prevAfterColumn = 0;
2313 while (true) {
2314 std::size_t posColumn = FindNonNestedNeedles(haystack.substr(prevAfterColumn), ":");
2315 if (posColumn == std::string_view::npos)
2316 return std::string_view::npos;
2318 // prevAfterColumn must have "::", i.e. two characters:
2319 if (prevAfterColumn + 1 >= lenHaystack)
2320 return std::string_view::npos;
2321
2322 ++prevAfterColumn; // done with first (or only) ':'
2323 if (haystack[prevAfterColumn] == ':')
2324 return prevAfterColumn - 1;
2325 ++prevAfterColumn; // That was not a ':'.
2326 }
2327
2328 return std::string_view::npos;
2329 }
2330
2331 std::string_view StripSurroundingSpace(std::string_view str)
2332 {
2333 while (!str.empty() && std::isspace(str[0]))
2334 str.remove_prefix(1);
2335 while (!str.empty() && std::isspace(str.back()))
2336 str.remove_suffix(1);
2337 return str;
2338 }
2339
2340 std::string ToString(std::string_view sv)
2341 {
2342 // ROOT's string_view backport doesn't add the new std::string contructor and assignment;
2343 // convert to std::string instead and assign that.
2344 return std::string(sv.data(), sv.length());
2345 }
2346} // unnamed namespace
2347
2348/// Split a function declaration into its different parts.
2350{
2351 // General structure:
2352 // `...` last-space `...` (`...`)
2353 // The first `...` is the return type.
2354 // The second `...` is the (possibly scoped) function name.
2355 // The third `...` are the parameters.
2356 // The function name can be of the form `...`<`...`>
2357 std::size_t posArgs = FindNonNestedNeedles(decl, "(");
2358 std::string_view declNoArgs = decl.substr(0, posArgs);
2359
2360 std::size_t prevAfterWhiteSpace = 0;
2361 static const char whitespace[] = " \t\n";
2362 while (declNoArgs.length() > prevAfterWhiteSpace) {
2364 if (posWS == std::string_view::npos)
2365 break;
2367 while (declNoArgs.length() > prevAfterWhiteSpace
2370 }
2371
2372 /// Include any '&*' in the return type:
2373 std::size_t endReturn = prevAfterWhiteSpace;
2374 while (declNoArgs.length() > endReturn
2375 && strchr("&* \t \n", declNoArgs[endReturn]))
2376 ++endReturn;
2377
2378 result.fReturnType = ToString(StripSurroundingSpace(declNoArgs.substr(0, endReturn)));
2379
2380 /// scope::anotherscope::functionName<tmplt>:
2381 std::string_view scopeFunctionTmplt = declNoArgs.substr(endReturn);
2383 while (prevAtScope != std::string_view::npos
2384 && scopeFunctionTmplt.length() > prevAtScope + 2) {
2386 if (posScope == std::string_view::npos)
2387 break;
2388 prevAtScope += posScope + 2;
2389 }
2390
2391 std::size_t afterScope = prevAtScope + 2;
2392 if (prevAtScope == std::string_view::npos) {
2393 afterScope = 0;
2394 prevAtScope = 0;
2395 }
2396
2397 result.fScopeName = ToString(StripSurroundingSpace(scopeFunctionTmplt.substr(0, prevAtScope)));
2398 std::string_view funcNameTmplArgs = scopeFunctionTmplt.substr(afterScope);
2399
2400 result.fFunctionTemplateArguments.clear();
2402 if (posTmpltOpen != std::string_view::npos) {
2403 result.fFunctionName = ToString(StripSurroundingSpace(funcNameTmplArgs.substr(0, posTmpltOpen)));
2404
2405 // Parse template parameters:
2406 std::string_view tmpltArgs = funcNameTmplArgs.substr(posTmpltOpen + 1);
2407 std::size_t posTmpltClose = FindNonNestedNeedles(tmpltArgs, ">");
2408 if (posTmpltClose != std::string_view::npos) {
2409 tmpltArgs = tmpltArgs.substr(0, posTmpltClose);
2410 std::size_t prevAfterArg = 0;
2411 while (tmpltArgs.length() > prevAfterArg) {
2412 std::size_t posComma = FindNonNestedNeedles(tmpltArgs.substr(prevAfterArg), ",");
2413 if (posComma == std::string_view::npos) {
2414 break;
2415 }
2416 result.fFunctionTemplateArguments.emplace_back(ToString(StripSurroundingSpace(tmpltArgs.substr(prevAfterArg, posComma))));
2417 prevAfterArg += posComma + 1;
2418 }
2419 // Add the trailing arg.
2420 result.fFunctionTemplateArguments.emplace_back(ToString(StripSurroundingSpace(tmpltArgs.substr(prevAfterArg))));
2421 }
2422 } else {
2423 result.fFunctionName = ToString(StripSurroundingSpace(funcNameTmplArgs));
2424 }
2425
2426 result.fFunctionParameters.clear();
2427 if (posArgs != std::string_view::npos) {
2428 /// (params)
2429 std::string_view params = decl.substr(posArgs + 1);
2430 std::size_t posEndArgs = FindNonNestedNeedles(params, ")");
2431 if (posEndArgs != std::string_view::npos) {
2432 params = params.substr(0, posEndArgs);
2433 std::size_t prevAfterArg = 0;
2434 while (params.length() > prevAfterArg) {
2435 std::size_t posComma = FindNonNestedNeedles(params.substr(prevAfterArg), ",");
2436 if (posComma == std::string_view::npos) {
2437 result.fFunctionParameters.emplace_back(ToString(StripSurroundingSpace(params.substr(prevAfterArg))));
2438 break;
2439 }
2440 result.fFunctionParameters.emplace_back(ToString(StripSurroundingSpace(params.substr(prevAfterArg, posComma))));
2441 prevAfterArg += posComma + 1; // skip ','
2442 }
2443 }
2444 }
2445
2446 return true;
2447}
#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:65
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()