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
908 // Remove the std:: and default template argument and insert the Long64_t and change basic_string to string.
911
912 // 4 elements expected: "pair", "first type name", "second type name", "trailing stars"
913 if (splitname.fElements.size() == 4 && (splitname.fElements[0] == "std::pair" || splitname.fElements[0] == "pair" || splitname.fElements[0] == "__pair_base")) {
914 // We don't want to lookup the std::pair itself.
915 std::string first, second;
916 GetNormalizedName(first, splitname.fElements[1]);
917 GetNormalizedName(second, splitname.fElements[2]);
918 norm_name = splitname.fElements[0] + "<" + first + "," + second;
919 if (!second.empty() && second.back() == '>')
920 norm_name += " >";
921 else
922 norm_name += ">";
923 return;
924 }
925
926 // Depending on how the user typed their code, in particular typedef
927 // declarations, we may end up with an explicit '::' being
928 // part of the result string. For consistency, we must remove it.
930
931 if (gInterpreterHelper) {
932 // See if the expanded name itself is a typedef.
933 std::string typeresult;
934 if (gInterpreterHelper->ExistingTypeCheck(norm_name, typeresult)
935 || gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(norm_name, typeresult)) {
936
937 if (!typeresult.empty()) {
938 // For STL containers, typeresult comes back with default template arguments, so a last
939 // stripping step is required
941 typeresult.c_str(),
946 }
947 }
948 }
949}
950
951////////////////////////////////////////////////////////////////////////////////
952/// Replace 'long long' and 'unsigned long long' by 'Long64_t' and 'ULong64_t'
953
955{
956 if (!original)
957 return "";
958 else
959 return GetLong64_Name(string(original));
960}
961
962////////////////////////////////////////////////////////////////////////////////
963/// Replace 'long long' and 'unsigned long long' by 'Long64_t' and 'ULong64_t'
964
966{
967 static const char* longlong_s = "long long";
968 static const char* ulonglong_s = "unsigned long long";
969 static const unsigned int longlong_len = strlen(longlong_s);
970 static const unsigned int ulonglong_len = strlen(ulonglong_s);
971
972 string result = original;
973
974 int pos = 0;
975 while( (pos = result.find(ulonglong_s,pos) ) >=0 ) {
976 result.replace(pos, ulonglong_len, "ULong64_t");
977 }
978 pos = 0;
979 while( (pos = result.find(longlong_s,pos) ) >=0 ) {
980 result.replace(pos, longlong_len, "Long64_t");
981 }
982 return result;
983}
984
985////////////////////////////////////////////////////////////////////////////////
986/// Return the start of the unqualified name include in 'original'.
987
989{
990 const char *lastPos = original;
991 {
992 long depth = 0;
993 for(auto cursor = original; *cursor != '\0'; ++cursor) {
994 if ( *cursor == '<' || *cursor == '(') ++depth;
995 else if ( *cursor == '>' || *cursor == ')' ) --depth;
996 else if ( *cursor == ':' ) {
997 if (depth==0 && *(cursor+1) == ':' && *(cursor+2) != '\0') {
998 lastPos = cursor+2;
999 }
1000 }
1001 }
1002 }
1003 return lastPos;
1004}
1005
1006////////////////////////////////////////////////////////////////////////////////
1007
1008static void R__FindTrailing(std::string &full, /*modified*/
1009 std::string &stars /* the literal output */
1010 )
1011{
1012 const char *t = full.c_str();
1013 const unsigned int tlen( full.size() );
1014
1015 const char *starloc = t + tlen - 1;
1016 bool hasconst = false;
1017 if ( (*starloc)=='t'
1018 && (starloc-t) > 4 && 0 == strncmp((starloc-4),"const",5)
1019 && ( (*(starloc-5)) == ' ' || (*(starloc-5)) == '*' || (*(starloc-5)) == '&'
1020 || (*(starloc-5)) == '>' || (*(starloc-5)) == ']') ) {
1021 // we are ending on a const.
1022 starloc -= 4;
1023 if ((*starloc-1)==' ') {
1024 // Take the space too.
1025 starloc--;
1026 }
1027 hasconst = true;
1028 }
1029 if ( hasconst || (*starloc)=='*' || (*starloc)=='&' || (*starloc)==']' ) {
1030 bool isArray = ( (*starloc)==']' );
1031 while( t<=(starloc-1) && ((*(starloc-1))=='*' || (*(starloc-1))=='&' || (*(starloc-1))=='t' || isArray)) {
1032 if (isArray) {
1033 starloc--;
1034 isArray = ! ( (*starloc)=='[' );
1035 } else if ( (*(starloc-1))=='t' ) {
1036 if ( (starloc-1-t) > 5 && 0 == strncmp((starloc-5),"const",5)
1037 && ( (*(starloc-6)) == ' ' || (*(starloc-6)) == '*' || (*(starloc-6)) == '&'
1038 || (*(starloc-6)) == '>' || (*(starloc-6)) == ']')) {
1039 // we have a const.
1040 starloc -= 5;
1041 } else {
1042 break;
1043 }
1044 } else {
1045 starloc--;
1046 }
1047 }
1048 stars = starloc;
1049 if ((*(starloc-1))==' ') {
1050 // erase the space too.
1051 starloc--;
1052 }
1053
1054 const unsigned int starlen = strlen(starloc);
1055 full.erase(tlen-starlen,starlen);
1056 } else if (hasconst) {
1057 stars = starloc;
1058 const unsigned int starlen = strlen(starloc);
1059 full.erase(tlen-starlen,starlen);
1060 }
1061
1062}
1063
1064////////////////////////////////////////////////////////////////////////////////
1065////////////////////////////////////////////////////////////////////////////
1066/// Stores in output (after emptying it) the split type.
1067/// Stores the location of the tail (nested names) in nestedLoc (0 indicates no tail).
1068/// Return the number of elements stored.
1069///
1070/// First in list is the template name or is empty
1071/// "vector<list<int>,alloc>**" to "vector" "list<int>" "alloc" "**"
1072/// or "TNamed*" to "" "TNamed" "*"
1073////////////////////////////////////////////////////////////////////////////
1074
1076{
1077 nestedLoc = 0;
1078 output.clear();
1079 if (strlen(type)==0) return 0;
1080
1081 int cleantypeMode = 1 /* keepInnerConst */;
1082 if (mode & kKeepOuterConst) {
1083 cleantypeMode = 0; /* remove only the outer class keyword */
1084 }
1087
1088 // We need to replace basic_string with string.
1089 {
1090 bool isString = false;
1091 bool isStdString = false;
1092 bool isConst = false;
1093 size_t prefix_offset = 0;
1094
1095 if (full.compare(prefix_offset, 6, "const ") == 0) {
1096 prefix_offset += isConst = true;
1097 }
1098 if (full.compare(prefix_offset, 5, "std::") == 0) {
1099 prefix_offset += 5;
1100 isStdString = true;
1101 }
1102 if (full.compare(prefix_offset, 9, "__cxx11::") == 0) {
1103 prefix_offset += 9;
1104 }
1105 if (full.compare(prefix_offset, 17, "basic_string<char") == 0) {
1106 isString = true;
1107 prefix_offset += 17;
1108 }
1109
1110 if (isString) {
1111 size_t offset = prefix_offset;
1112 if ( full[offset] == '>' ) {
1113 // done.
1114 } else if (full[offset] == ',') {
1115 ++offset;
1116 if (full.compare(offset, 5, "std::") == 0) {
1117 offset += 5;
1118 }
1119 constexpr auto char_traits_s = "char_traits<char>";
1120 // or
1121 // static constexpr char const* const char_traits_s = "char_traits<char>";
1122 static constexpr unsigned int char_traits_len = std::char_traits<char>::length(char_traits_s);
1123 if (full.compare(offset, char_traits_len, char_traits_s) == 0) {
1125 if ( full[offset] == '>') {
1126 // done.
1127 } else if (full[offset] == ' ' && full[offset+1] == '>') {
1128 ++offset;
1129 // done.
1130 } else if (full[offset] == ',') {
1131 ++offset;
1132 if (full.compare(offset, 5, "std::") == 0) {
1133 offset += 5;
1134 }
1135 static const char* allocator_s = "allocator<char>";
1136 static const unsigned int allocator_len = strlen(allocator_s);
1137 if (full.compare(offset, allocator_len, allocator_s) == 0) {
1139 if ( full[offset] == '>') {
1140 // done.
1141 } else if (full[offset] == ' ' && full[offset+1] == '>') {
1142 ++offset;
1143 // done.
1144 } else {
1145 // Not std::string
1146 isString = false;
1147 }
1148 }
1149 } else {
1150 // Not std::string
1151 isString = false;
1152 }
1153 } else {
1154 // Not std::string.
1155 isString = false;
1156 }
1157 } else {
1158 // Not std::string.
1159 isString = false;
1160 }
1161 if (isString) {
1162 output.push_back(string());
1163 if (isConst && (mode & kKeepOuterConst)) {
1164 if (isStdString && !(mode & kDropStd)) {
1165 output.push_back("const std::string");
1166 } else {
1167 output.push_back("const string");
1168 }
1169 } else {
1170 if (isStdString && !(mode & kDropStd)) {
1171 output.push_back("std::string");
1172 } else {
1173 output.push_back("string");
1174 }
1175 }
1176 if (offset < full.length()) {
1177 // Copy the trailing text.
1178 // keep the '>' inside right for R__FindTrailing to work
1179 string right( full.substr(offset) );
1180 string stars;
1181 R__FindTrailing(right, stars);
1182 output.back().append(right.c_str()+1); // skip the '>'
1183 output.push_back(stars);
1184 } else {
1185 output.push_back("");
1186 }
1187 return output.size();
1188 }
1189 }
1190 }
1191
1192 if ( mode & kDropStd) {
1193 unsigned int offset = (0==strncmp("const ",full.c_str(),6)) ? 6 : 0;
1194 RemoveStd( full, offset );
1195 }
1196
1197 string stars;
1198 if ( !full.empty() ) {
1199 R__FindTrailing(full, stars);
1200 }
1201
1202 const char *c = strchr(full.c_str(),'<');
1203 if (c) {
1204 //we have 'something<'
1205 output.push_back(string(full,0,c - full.c_str()));
1206
1207 const char *cursor;
1208 int level = 0;
1209 int parenthesis = 0;
1210 for(cursor = c + 1; *cursor != '\0' && !(level==0 && *cursor == '>'); ++cursor) {
1211 if (*cursor == '(') {
1212 ++parenthesis;
1213 continue;
1214 } else if (*cursor == ')') {
1215 --parenthesis;
1216 continue;
1217 }
1218 if (parenthesis)
1219 continue;
1220 switch (*cursor) {
1221 case '<': ++level; break;
1222 case '>': --level; break;
1223 case ',':
1224 if (level == 0) {
1225 output.push_back(std::string(c+1,cursor));
1226 c = cursor;
1227 }
1228 break;
1229 }
1230 }
1231 if (*cursor=='>') {
1232 if (*(cursor-1) == ' ') {
1233 output.push_back(std::string(c+1,cursor-1));
1234 } else {
1235 output.push_back(std::string(c+1,cursor));
1236 }
1237 // See what's next!
1238 if (*(cursor+1)==':') {
1239 // we have a name specified inside the class/namespace
1240 // For now we keep it in one piece
1241 nestedLoc = output.size();
1242 output.push_back((cursor+1));
1243 }
1244 } else if (level >= 0) {
1245 // Unterminated template
1246 output.push_back(std::string(c+1,cursor));
1247 }
1248 } else {
1249 //empty
1250 output.push_back(string());
1251 output.push_back(full);
1252 }
1253
1254 if (!output.empty()) output.push_back(stars);
1255 return output.size();
1256}
1257
1258
1259////////////////////////////////////////////////////////////////////////////////
1260////////////////////////////////////////////////////////////////////////////
1261/// Cleanup type description, redundant blanks removed
1262/// and redundant tail ignored
1263/// return *tail = pointer to last used character
1264/// if (mode==0) keep keywords
1265/// if (mode==1) remove keywords outside the template params
1266/// if (mode>=2) remove the keywords everywhere.
1267/// if (tail!=0) cut before the trailing *
1268///
1269/// The keywords currently are: "const" , "volatile" removed
1270///
1271///
1272/// CleanType(" A<B, C< D, E> > *,F,G>") returns "A<B,C<D,E> >*"
1273////////////////////////////////////////////////////////////////////////////
1274
1275string TClassEdit::CleanType(const char *typeDesc, int mode, const char **tail)
1276{
1277 constexpr static std::array<const char *, 3> remove{"class", "const", "volatile"};
1278 constexpr static auto lengths = []() constexpr {
1279 std::array<std::size_t, std::size(remove)> ret{};
1280 for (std::size_t i = 0; i < remove.size(); i++)
1281 ret[i] = std::char_traits<char>::length(remove[i]);
1282 return ret;
1283 }();
1284
1285 string result;
1286 result.reserve(strlen(typeDesc)*2);
1287 int lev=0,kbl=1;
1288 const char* c;
1289
1290 for(c=typeDesc;*c;c++) {
1291 if (c[0]==' ') {
1292 if (kbl) continue;
1293 if (!isalnum(c[ 1]) && c[ 1] !='_') continue;
1294 }
1295 if (kbl && (mode>=2 || lev==0)) { //remove "const' etc...
1296 int done = 0;
1297 size_t n = (mode) ? std::size(remove) : 1;
1298
1299 // loop on all the keywords we want to remove
1300 for (size_t k = 0; k < n; k++) {
1301 auto rlen = lengths[k];
1302
1303 // Do we have a match
1304 if (strncmp(remove[k],c,rlen)) continue;
1305
1306 // make sure that the 'keyword' is not part of a longer indentifier
1307 if (isalnum(c[rlen]) || c[rlen]=='_' || c[rlen]=='$') continue;
1308
1309 c+=rlen-1; done = 1; break;
1310 }
1311 if (done) continue;
1312 }
1313
1314 kbl = (!isalnum(c[ 0]) && c[ 0]!='_' && c[ 0]!='$' && c[0]!='[' && c[0]!=']' && c[0]!='-' && c[0]!='@');
1315 // '@' is special character used only the artifical class name used by ROOT to implement the
1316 // I/O customization rules that requires caching of the input data.
1317
1318 if (*c == '<' || *c == '(') lev++;
1319 if (lev==0 && !isalnum(*c)) {
1320 if (!strchr("*&:._$ []-@",*c)) break;
1321 // '.' is used as a module/namespace separator by PyROOT, see
1322 // TPyClassGenerator::GetClass.
1323 }
1324 if (c[0]=='>' && result.size() && result[result.size()-1]=='>') result+=" ";
1325
1326 result += c[0];
1327
1328 if (*c == '>' || *c == ')') lev--;
1329 }
1330 if(tail) *tail=c;
1331 return result;
1332}
1333
1334////////////////////////////////////////////////////////////////////////////////
1335//////////////////////////////////////////////////////////////////////////////
1336/// Return the absolute type of typeDesc.
1337/// E.g.: typeDesc = "class const volatile TNamed**", returns "TNamed**".
1338/// if (mode&1) remove last "*"s returns "TNamed"
1339/// if (mode&2) remove default allocators from STL containers
1340/// if (mode&4) remove all allocators from STL containers
1341/// if (mode&8) return inner class of stl container. list<innerClass>
1342/// if (mode&16) return deapest class of stl container. vector<list<deapest>>
1343/// if (mode&kDropAllDefault) remove default template arguments
1344//////////////////////////////////////////////////////////////////////////////
1345
1346string TClassEdit::ShortType(const char *typeDesc, int mode)
1347{
1348 string answer;
1349
1350 // get list of all arguments
1351 if (typeDesc) {
1353 arglist.ShortType(answer, mode);
1354 }
1355
1356 return answer;
1357}
1358
1359////////////////////////////////////////////////////////////////////////////////
1360/// Return true if the type is one the interpreter details which are
1361/// only forward declared (ClassInfo_t etc..)
1362
1364{
1365 size_t len = strlen(type);
1366 if (len < 2 || strncmp(type+len-2,"_t",2) != 0) return false;
1367
1368 unsigned char offset = 0;
1369 if (strncmp(type,"const ",6)==0) { offset += 6; }
1370 static const char *names[] = { "CallFunc_t","ClassInfo_t","BaseClassInfo_t",
1371 "DataMemberInfo_t","FuncTempInfo_t","MethodInfo_t","MethodArgInfo_t",
1372 "TypeInfo_t", "TypedefInfo_t", nullptr};
1373
1374 for(int k=1;names[k];k++) {if (strcmp(type+offset,names[k])==0) return true;}
1375 return false;
1376}
1377
1378////////////////////////////////////////////////////////////////////////////////
1379/// Return true is the name is std::bitset<number> or bitset<number>
1380
1381bool TClassEdit::IsSTLBitset(const char *classname)
1382{
1383 size_t offset = StdLen(classname);
1384 if ( strncmp(classname+offset,"bitset<",std::char_traits<char>::length("bitset<"))==0) return true;
1385 return false;
1386}
1387
1388////////////////////////////////////////////////////////////////////////////////
1389/// Return the type of STL collection, if any, that is the underlying type
1390/// of the given type. Namely return the value of IsSTLCont after stripping
1391/// pointer, reference and constness from the type.
1392/// UnderlyingIsSTLCont("vector<int>*") == IsSTLCont("vector<int>")
1393/// See TClassEdit::IsSTLCont
1394///
1395/// type : type name: vector<list<classA,allocator>,allocator>*
1396/// result: 0 : not stl container
1397/// code of container 1=vector,2=list,3=deque,4=map
1398/// 5=multimap,6=set,7=multiset
1399
1401{
1402 if (type.compare(0,6,"const ",6) == 0)
1403 type.remove_prefix(6);
1404
1405 while(type[type.length()-1]=='*' ||
1406 type[type.length()-1]=='&' ||
1407 type[type.length()-1]==' ') {
1408 type.remove_suffix(1);
1409 }
1410 return IsSTLCont(type);
1411}
1412
1413////////////////////////////////////////////////////////////////////////////////
1414/// type : type name: vector<list<classA,allocator>,allocator>
1415/// result: 0 : not stl container
1416/// code of container 1=vector,2=list,3=deque,4=map
1417/// 5=multimap,6=set,7=multiset
1418
1420{
1421 auto pos = type.find('<');
1422 if (pos==std::string_view::npos) return ROOT::kNotSTL;
1423
1424 auto c = pos+1;
1425 for (decltype(type.length()) level = 1; c < type.length(); ++c) {
1426 if (type[c] == '<') ++level;
1427 if (type[c] == '>') --level;
1428 if (level == 0) break;
1429 }
1430 if (c != (type.length()-1) ) {
1431 return ROOT::kNotSTL;
1432 }
1433
1434 return STLKind(type.substr(0,pos));
1435}
1436
1437////////////////////////////////////////////////////////////////////////////////
1438/// type : type name: vector<list<classA,allocator>,allocator>
1439/// testAlloc: if true, we test allocator, if it is not default result is negative
1440/// result: 0 : not stl container
1441/// abs(result): code of container 1=vector,2=list,3=deque,4=map
1442/// 5=multimap,6=set,7=multiset
1443/// positive val: we have a vector or list with default allocator to any depth
1444/// like vector<list<vector<int>>>
1445/// negative val: STL container other than vector or list, or non default allocator
1446/// For example: vector<deque<int>> has answer -1
1447
1449{
1450 if (!strchr(type,'<')) return 0;
1451
1453 return arglist.IsSTLCont(testAlloc);
1454}
1455
1456////////////////////////////////////////////////////////////////////////////////
1457/// return true if the class belongs to the std namespace
1458
1459bool TClassEdit::IsStdClass(const char *classname)
1460{
1461 constexpr auto length = std::char_traits<char>::length;
1462 classname += StdLen( classname );
1463 if ( strcmp(classname,"string")==0 ) return true;
1464 if ( strncmp(classname,"bitset<",length("bitset<"))==0) return true;
1465 if ( IsStdPair(classname) ) return true;
1466 if ( strcmp(classname,"allocator")==0) return true;
1467 if ( strncmp(classname,"allocator<",length("allocator<"))==0) return true;
1468 if ( strncmp(classname,"greater<",length("greater<"))==0) return true;
1469 if ( strncmp(classname,"less<",length("less<"))==0) return true;
1470 if ( strncmp(classname,"equal_to<",length("equal_to<"))==0) return true;
1471 if ( strncmp(classname,"hash<",length("hash<"))==0) return true;
1472 if ( strncmp(classname,"auto_ptr<",length("auto_ptr<"))==0) return true;
1473
1474 if ( strncmp(classname,"vector<",length("vector<"))==0) return true;
1475 if ( strncmp(classname,"list<",length("list<"))==0) return true;
1476 if ( strncmp(classname,"forward_list<",length("forward_list<"))==0) return true;
1477 if ( strncmp(classname,"deque<",length("deque<"))==0) return true;
1478 if ( strncmp(classname,"map<",length("map<"))==0) return true;
1479 if ( strncmp(classname,"multimap<",length("multimap<"))==0) return true;
1480 if ( strncmp(classname,"set<",length("set<"))==0) return true;
1481 if ( strncmp(classname,"multiset<",length("multiset<"))==0) return true;
1482 if ( strncmp(classname,"unordered_set<",length("unordered_set<"))==0) return true;
1483 if ( strncmp(classname,"unordered_multiset<",length("unordered_multiset<"))==0) return true;
1484 if ( strncmp(classname,"unordered_map<",length("unordered_map<"))==0) return true;
1485 if ( strncmp(classname,"unordered_multimap<",length("unordered_multimap<"))==0) return true;
1486 if ( strncmp(classname,"bitset<",length("bitset<"))==0) return true;
1487 if ( strncmp(classname,"ROOT::VecOps::RVec<",length("ROOT::VecOps::RVec<"))==0) return true;
1488
1489 return false;
1490}
1491
1492
1493////////////////////////////////////////////////////////////////////////////////
1494
1497
1498 return ( TClassEdit::STLKind( splitname.fElements[0] ) == ROOT::kSTLvector)
1499 && ( splitname.fElements[1] == "bool" || splitname.fElements[1]=="Bool_t");
1500}
1501
1502////////////////////////////////////////////////////////////////////////////////
1503
1504static void ResolveTypedefProcessType(const char *tname,
1505 unsigned int /* len */,
1506 unsigned int cursor,
1507 bool constprefix,
1508 unsigned int start_of_type,
1509 unsigned int end_of_type,
1510 unsigned int mod_start_of_type,
1511 bool &modified,
1512 std::string &result)
1513{
1514 std::string type(modified && (mod_start_of_type < result.length()) ?
1515 result.substr(mod_start_of_type, string::npos)
1516 : 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
1517 string typeresult;
1518 if (gInterpreterHelper->ExistingTypeCheck(type, typeresult)
1519 || gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(type, typeresult, false)) {
1520 // it is a known type
1521 if (!typeresult.empty()) {
1522 // and it is a typedef, we need to replace it in the output.
1523 if (modified) {
1524 result.replace(mod_start_of_type, string::npos,
1525 typeresult);
1526 }
1527 else {
1528 modified = true;
1529 result += string(tname,0,start_of_type);
1530 if (constprefix && typeresult.compare(0,6,"const ",6) == 0) {
1531 result += typeresult.substr(6,string::npos);
1532 } else {
1533 result += typeresult;
1534 }
1535 }
1536 } else if (modified) {
1537 result.replace(mod_start_of_type, string::npos,
1538 type);
1539 }
1540 if (modified) {
1541 if (end_of_type != 0 && end_of_type!=cursor) {
1542 result += std::string(tname,end_of_type,cursor-end_of_type);
1543 }
1544 }
1545 } else {
1546 // no change needed.
1547 if (modified) {
1548 // result += type;
1549 if (end_of_type != 0 && end_of_type!=cursor) {
1550 result += std::string(tname,end_of_type,cursor-end_of_type);
1551 }
1552 }
1553 }
1554}
1555
1556////////////////////////////////////////////////////////////////////////////////
1557
1558static void ResolveTypedefImpl(const char *tname,
1559 unsigned int len,
1560 unsigned int &cursor,
1561 bool &modified,
1562 std::string &result)
1563{
1564 // Need to parse and deal with
1565 // A::B::C< D, E::F, G::H<I,J>::K::L >::M
1566 // where E might be replace by N<O,P>
1567 // and G::H<I,J>::K or G might be a typedef.
1568
1569 bool constprefix = false;
1570
1571 if (tname[cursor]==' ') {
1572 if (!modified) {
1573 modified = true;
1574 result += string(tname,0,cursor);
1575 }
1576 while (tname[cursor]==' ') ++cursor;
1577 }
1578 // In Windows, we might have 'class const ...' as name,
1579 // (never 'const class ...'), so skip the leading 'class ', if any
1580 if (strncmp(tname+cursor,"class ",6) == 0) {
1581 cursor += 6;
1582 }
1583 if (strncmp(tname+cursor,"const ",6) == 0) {
1584 cursor += 6;
1585 if (modified) result += "const ";
1586 constprefix = true;
1587 }
1588
1589 if (len > 2 && strncmp(tname+cursor,"::",2) == 0) {
1590 cursor += 2;
1591 }
1592
1593 unsigned int start_of_type = cursor;
1594 unsigned int end_of_type = 0;
1595 unsigned int mod_start_of_type = result.length();
1596 unsigned int prevScope = cursor;
1597 for ( ; cursor<len; ++cursor) {
1598 switch (tname[cursor]) {
1599 case ':': {
1600 if ((cursor+1)>=len || tname[cursor+1]!=':') {
1601 // we expected another ':', malformed, give up.
1602 if (modified) result += (tname+prevScope);
1603 return;
1604 }
1605 string scope;
1606 if (modified) {
1607 scope = result.substr(mod_start_of_type, string::npos);
1608 scope += std::string(tname+prevScope,cursor-prevScope);
1609 } else {
1610 scope = std::string(tname, start_of_type, cursor - start_of_type); // we need to try to avoid this copy
1611 }
1612 std::string scoperesult;
1613 bool isInlined = false;
1614 if (gInterpreterHelper->ExistingTypeCheck(scope, scoperesult)
1615 ||gInterpreterHelper->GetPartiallyDesugaredNameWithScopeHandling(scope, scoperesult)) {
1616 // it is a known type
1617 if (!scoperesult.empty()) {
1618 // and it is a typedef
1619 if (modified) {
1620 if (constprefix && scoperesult.compare(0,6,"const ",6) != 0) mod_start_of_type -= 6;
1621 result.replace(mod_start_of_type, string::npos,
1622 scoperesult);
1623 result += "::";
1624 } else {
1625 modified = true;
1627 result += string(tname,0,start_of_type);
1628 //if (constprefix) result += "const ";
1630 result += "::";
1631 }
1632 } else if (modified) {
1633 result += std::string(tname+prevScope,cursor+2-prevScope);
1634 }
1635 } else if (!gInterpreterHelper->IsDeclaredScope(scope,isInlined)) {
1636 // the nesting namespace is not declared, just ignore it and move on
1637 if (modified) result += std::string(tname+prevScope,cursor+2-prevScope);
1638 } else if (isInlined) {
1639 // humm ... just skip it.
1640 if (!modified) {
1641 modified = true;
1643 result += string(tname,0,start_of_type);
1644 //if (constprefix) result += "const ";
1646 }
1647 } else if (modified) {
1648 result += std::string(tname+prevScope,cursor+2-prevScope);
1649 }
1650 // Consume the 1st semi colon, the 2nd will be consume by the for loop.
1651 ++cursor;
1652 prevScope = cursor+1;
1653 break;
1654 }
1655 case '(':
1656 case '<': {
1657 // We move on in presence of function pointers, i.e. if we find '(*)' (with spaces which could be in
1658 // between), for example cases like:
1659 // pair<dd4hep::sim::Geant4Sensitive*,Geant4HitCollection*(*)(const std::string&,const std::string&,Geant4Sensitive*)>
1660 // This honors #18842 without breaking #18833.
1661 auto nStars = 0u;
1662 auto next = cursor + 1;
1663 for (; next != cursor && nStars < 2 && next < len; next++) {
1664 if (' ' == tname[next]) {
1665 // We simply skip spaces
1666 continue;
1667 } else if ('*' == tname[next]) {
1668 nStars++;
1669 } else if (')' == tname[next]) {
1670 if (nStars == 1) {
1671 cursor = next;
1672 } else {
1673 break;
1674 }
1675 } else {
1676 // if the token is not ' ', '*', or ')' we move on
1677 break;
1678 }
1679 }
1680 // If we found '(*)' (with potentially spaces in between the characters)
1681 // we move on with the parsing
1682 if (cursor == next)
1683 break;
1684
1685 // push information on stack
1686 if (modified) {
1687 result += std::string(tname+prevScope,cursor+1-prevScope);
1688 // above includes the '<' .... result += '<';
1689 }
1690 do {
1691 ++cursor;
1693 } while( cursor<len && tname[cursor] == ',' );
1694
1695 while (cursor<len && tname[cursor+1]==' ') ++cursor;
1696
1697 // Since we already checked the type, skip the next section
1698 // (respective the scope section and final type processing section)
1699 // as they would re-do the same job.
1700 if (cursor+2<len && tname[cursor+1]==':' && tname[cursor+2]==':') {
1701 if (modified) result += "::";
1702 cursor += 2;
1703 prevScope = cursor+1;
1704 }
1705 if ( (cursor+1)<len && tname[cursor+1] == ',') {
1706 ++cursor;
1707 if (modified) result += ',';
1708 return;
1709 }
1710 if ( (cursor+1)<len && tname[cursor+1] == '>') {
1711 ++cursor;
1712 if (modified) result += " >";
1713 return;
1714 }
1715 if ( (cursor+1)<len && tname[cursor+1] == ')') {
1716 ++cursor;
1717 if (modified) result += ")";
1718 return;
1719 }
1720 if ( (cursor+1) >= len) {
1721 return;
1722 }
1723 if (tname[cursor] != ' ') break;
1724 if (modified) prevScope = cursor+1;
1725 // If the 'current' character is a space we need to treat it,
1726 // since this the next case statement, we can just fall through,
1727 // otherwise we should need to do:
1728 // --cursor; break;
1729 }
1730 case ' ': {
1732 // let's see if we have 'long long' or 'unsigned int' or 'signed char' or what not.
1733 while ((cursor+1)<len && tname[cursor+1] == ' ') ++cursor;
1734
1735 auto next = cursor+1;
1736 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] == ']'))
1737 {
1738 // A first const after the type needs to be move in the front.
1739 if (!modified) {
1740 modified = true;
1741 result += string(tname,0,start_of_type);
1742 result += "const ";
1745 } else if (mod_start_of_type < result.length()) {
1746 result.insert(mod_start_of_type,"const ");
1747 mod_start_of_type += 6;
1748 } else {
1749 result += "const ";
1750 mod_start_of_type += 6;
1752 }
1753 cursor += 5;
1754 end_of_type = cursor+1;
1756 if ((next+5)==len || tname[next+5] == ',' || tname[next+5] == '>' || tname[next+5] == ')' || tname[next+5] == '[') {
1757 break;
1758 }
1759 } else if (next!=len && tname[next] != '*' && tname[next] != '&') {
1760 // the type is not ended yet.
1761 end_of_type = 0;
1762 break;
1763 }
1764 ++cursor;
1765 // Intentional fall through;
1766 }
1767 case '*':
1768 case '&': {
1769 if (tname[cursor] != ' ') end_of_type = cursor;
1770 // check and skip const (followed by *,&, ,) ... what about followed by ':','['?
1771 auto next = cursor+1;
1772 if (strncmp(tname+next,"const",5) == 0) {
1773 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] == '[') {
1774 next += 5;
1775 }
1776 }
1777 while (next<len &&
1778 (tname[next] == ' ' || tname[next] == '*' || tname[next] == '&')) {
1779 ++next;
1780 // check and skip const (followed by *,&, ,) ... what about followed by ':','['?
1781 if (strncmp(tname+next,"const",5) == 0) {
1782 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] == '[') {
1783 next += 5;
1784 }
1785 }
1786 }
1787 cursor = next-1;
1788// if (modified && mod_start_of_type < result.length()) {
1789// result += string(tname,end_of_type,cursor-end_of_type);
1790// }
1791 break;
1792 }
1793 case ',': {
1794 if (modified && prevScope) {
1795 result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1796 }
1798 modified, result);
1799 if (modified) result += ',';
1800 return;
1801 }
1802 case ')':
1803 case '>': {
1804 char c = tname[cursor];
1805 if (modified && prevScope) {
1806 result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1807 }
1809 modified, result);
1810 if (modified) result += c;
1811 return;
1812 }
1813 default:
1814 end_of_type = 0;
1815 }
1816 }
1817
1818 if (prevScope && modified) result += std::string(tname+prevScope,(end_of_type == 0 ? cursor : end_of_type)-prevScope);
1819
1821 modified, result);
1822}
1823
1824
1825////////////////////////////////////////////////////////////////////////////////
1826
1827string TClassEdit::ResolveTypedef(const char *tname, bool /* resolveAll */)
1828{
1829 // Return the name of type 'tname' with all its typedef components replaced
1830 // by the actual type its points to
1831 // For example for "typedef MyObj MyObjTypedef;"
1832 // vector<MyObjTypedef> return vector<MyObj>
1833 //
1834
1835 if (!tname || *tname == 0)
1836 return "";
1837 if (!gInterpreterHelper)
1838 return tname;
1839
1840 std::string result;
1841
1842 // Check if we already know it is a normalized typename or a registered
1843 // typedef (i.e. known to gROOT).
1844 if (gInterpreterHelper->ExistingTypeCheck(tname, result))
1845 {
1846 if (result.empty()) return tname;
1847 else return result;
1848 }
1849
1850 unsigned int len = strlen(tname);
1851
1852 unsigned int cursor = 0;
1853 bool modified = false;
1855
1856 if (!modified) return tname;
1857 else return result;
1858}
1859
1860
1861////////////////////////////////////////////////////////////////////////////////
1862
1863string TClassEdit::InsertStd(const char *tname)
1864{
1865 // Return the name of type 'tname' with all STL classes prepended by "std::".
1866 // For example for "vector<set<auto_ptr<int*> > >" it returns
1867 // "std::vector<std::set<std::auto_ptr<int*> > >"
1868 //
1869
1870 static const char* sSTLtypes[] = {
1871 "allocator",
1872 "auto_ptr",
1873 "bad_alloc",
1874 "bad_cast",
1875 "bad_exception",
1876 "bad_typeid",
1877 "basic_filebuf",
1878 "basic_fstream",
1879 "basic_ifstream",
1880 "basic_ios",
1881 "basic_iostream",
1882 "basic_istream",
1883 "basic_istringstream",
1884 "basic_ofstream",
1885 "basic_ostream",
1886 "basic_ostringstream",
1887 "basic_streambuf",
1888 "basic_string",
1889 "basic_stringbuf",
1890 "basic_stringstream",
1891 "binary_function",
1892 "binary_negate",
1893 "bitset",
1894 "char_traits",
1895 "codecvt_byname",
1896 "codecvt",
1897 "collate",
1898 "collate_byname",
1899 "compare",
1900 "complex",
1901 "ctype_byname",
1902 "ctype",
1903 "deque",
1904 "divides",
1905 "domain_error",
1906 "equal_to",
1907 "exception",
1908 "forward_list",
1909 "fpos",
1910 "greater_equal",
1911 "greater",
1912 "gslice_array",
1913 "gslice",
1914 "hash",
1915 "indirect_array",
1916 "invalid_argument",
1917 "ios_base",
1918 "istream_iterator",
1919 "istreambuf_iterator",
1920 "istrstream",
1921 "iterator_traits",
1922 "iterator",
1923 "length_error",
1924 "less_equal",
1925 "less",
1926 "list",
1927 "locale",
1928 "localedef utility",
1929 "locale utility",
1930 "logic_error",
1931 "logical_and",
1932 "logical_not",
1933 "logical_or",
1934 "map",
1935 "mask_array",
1936 "mem_fun",
1937 "mem_fun_ref",
1938 "messages",
1939 "messages_byname",
1940 "minus",
1941 "modulus",
1942 "money_get",
1943 "money_put",
1944 "moneypunct",
1945 "moneypunct_byname",
1946 "multimap",
1947 "multiplies",
1948 "multiset",
1949 "negate",
1950 "not_equal_to",
1951 "num_get",
1952 "num_put",
1953 "numeric_limits",
1954 "numpunct",
1955 "numpunct_byname",
1956 "ostream_iterator",
1957 "ostreambuf_iterator",
1958 "ostrstream",
1959 "out_of_range",
1960 "overflow_error",
1961 "pair",
1962 "plus",
1963 "pointer_to_binary_function",
1964 "pointer_to_unary_function",
1965 "priority_queue",
1966 "queue",
1967 "range_error",
1968 "raw_storage_iterator",
1969 "reverse_iterator",
1970 "runtime_error",
1971 "set",
1972 "slice_array",
1973 "slice",
1974 "stack",
1975 "string",
1976 "strstream",
1977 "strstreambuf",
1978 "time_get_byname",
1979 "time_get",
1980 "time_put_byname",
1981 "time_put",
1982 "unary_function",
1983 "unary_negate",
1984 "unique_pointer",
1985 "underflow_error",
1986 "unordered_map",
1987 "unordered_multimap",
1988 "unordered_multiset",
1989 "unordered_set",
1990 "valarray",
1991 "vector",
1992 "wstring"
1993 };
1994
1995 if (!tname || *tname == 0) return "";
1996
1997 auto initSetSTLtypes = []() {
1998 std::set<std::string> iSetSTLtypes;
1999 // set up static set
2000 const size_t nSTLtypes = sizeof(sSTLtypes) / sizeof(const char*);
2001 for (size_t i = 0; i < nSTLtypes; ++i)
2002 iSetSTLtypes.insert(sSTLtypes[i]);
2003 return iSetSTLtypes;
2004 };
2006
2007 size_t b = 0;
2008 size_t len = strlen(tname);
2009 string ret;
2010 ret.reserve(len + 20); // expect up to 4 extra "std::" to insert
2011 string id;
2012 while (b < len) {
2013 // find beginning of next identifier
2014 bool precScope = false; // whether the identifier was preceded by "::"
2015 while (!(isalnum(tname[b]) || tname[b] == '_') && b < len) {
2016 precScope = (b < len - 2) && (tname[b] == ':') && (tname[b + 1] == ':');
2017 if (precScope) {
2018 ret += "::";
2019 b += 2;
2020 } else
2021 ret += tname[b++];
2022 }
2023
2024 // now b is at the beginning of an identifier or len
2025 size_t e = b;
2026 // find end of identifier
2027 id.clear();
2028 while (e < len && (isalnum(tname[e]) || tname[e] == '_'))
2029 id += tname[e++];
2030 if (!id.empty()) {
2031 if (!precScope) {
2032 set<string>::const_iterator iSTLtype = sSetSTLtypes.find(id);
2033 if (iSTLtype != sSetSTLtypes.end())
2034 ret += "std::";
2035 }
2036
2037 ret += id;
2038 b = e;
2039 }
2040 }
2041 return ret;
2042}
2043
2044////////////////////////////////////////////////////////////////////////////////
2045/// An helper class to dismount the name and remount it changed whenever
2046/// necessary
2047
2049 std::string fName;
2050 std::vector<std::unique_ptr<NameCleanerForIO>> fArgumentNodes = {};
2052 bool fHasChanged = false;
2054 {
2055 NameCleanerForIO* mother = fMother;
2056 if (!mother) return false;
2057 bool isSTLContOrArray = true;
2058 while (nullptr != mother){
2059 auto stlType = TClassEdit::IsSTLCont(mother->fName+"<>");
2061 mother = mother->fMother;
2062 }
2063
2064 return isSTLContOrArray;
2065 }
2066
2067public:
2068 NameCleanerForIO(const std::string& clName = "",
2070 NameCleanerForIO* mother = nullptr):fMother(mother)
2071 {
2072 if (clName.back() != '>') {
2073 fName = clName;
2074 return;
2075 }
2076
2077 std::vector<std::string> v;
2078 int dummy=0;
2079 TClassEdit::GetSplit(clName.c_str(), v, dummy, mode);
2080
2081 // We could be in presence of templates such as A1<T1>::A2<T2>::A3<T3>
2082 auto argsEnd = v.end();
2083 auto argsBeginPlusOne = ++v.begin();
2084 auto argPos = std::find_if(argsBeginPlusOne, argsEnd,
2085 [](std::string& arg){return (!arg.empty() && arg.front() == ':');});
2086 if (argPos != argsEnd) {
2087 const int length = clName.size();
2088 int wedgeBalance = 0;
2089 int lastOpenWedge = 0;
2090 for (int i=length-1;i>-1;i--) {
2091 auto& c = clName.at(i);
2092 if (c == '<') {
2093 wedgeBalance++;
2094 lastOpenWedge = i;
2095 } else if (c == '>') {
2096 wedgeBalance--;
2097 } else if (c == ':' && 0 == wedgeBalance) {
2098 // This would be A1<T1>::A2<T2>
2099 auto nameToClean = clName.substr(0,i-1);
2101 auto cleanName = node.ToString();
2102 fHasChanged = node.HasChanged();
2103 // We got A1<T1>::A2<T2> cleaned
2104
2105 // We build the changed A1<T1>::A2<T2>::A3
2106 cleanName += "::";
2107 // Now we get A3 and append it
2108 cleanName += clName.substr(i+1,lastOpenWedge-i-1);
2109
2110 // We now get the args of what in our case is A1<T1>::A2<T2>::A3
2111 auto lastTemplate = &clName.data()[i+1];
2112
2113 // We split it
2115 // We now replace the name of the template
2116 v[0] = cleanName;
2117 break;
2118 }
2119 }
2120 }
2121
2122 fName = v.front();
2123 unsigned int nargs = v.size() - 2;
2124 for (unsigned int i=0;i<nargs;++i) {
2125 fArgumentNodes.emplace_back(new NameCleanerForIO(v[i+1],mode,this));
2126 }
2127 }
2128
2129 bool HasChanged() const {return fHasChanged;}
2130
2131 std::string ToString()
2132 {
2133 std::string name(fName);
2134
2135 if (fArgumentNodes.empty()) return name;
2136
2137 // We have in hands a case like unique_ptr< ... >
2138 // Perhaps we could treat atomics as well like this?
2139 if (!fMother && TClassEdit::IsUniquePtr(fName+"<")) {
2140 name = fArgumentNodes.front()->ToString();
2141 // ROOT-9933: we remove const if present.
2143 tst.ShortType(name, 1);
2144 name += "*";
2145 fHasChanged = true;
2146 return name;
2147 }
2148
2149 // Now we treat the case of the collections of unique ptrs
2150 auto stlContType = AreAncestorsSTLContOrArray();
2151 if (stlContType != ROOT::kNotSTL && TClassEdit::IsUniquePtr(fName+"<")) {
2152 name = fArgumentNodes.front()->ToString();
2153 name += "*";
2154 fHasChanged = true;
2155 return name;
2156 }
2157
2158 name += "<";
2159 for (auto& node : fArgumentNodes) {
2160 name += node->ToString() + ",";
2161 fHasChanged |= node->HasChanged();
2162 }
2163 name.pop_back(); // Remove the last comma.
2164 name += name.back() == '>' ? " >" : ">"; // Respect name normalisation
2165 return name;
2166 }
2167
2168 const std::string& GetName() {return fName;}
2169 const std::vector<std::unique_ptr<NameCleanerForIO>>* GetChildNodes() const {return &fArgumentNodes;}
2170};
2171
2172////////////////////////////////////////////////////////////////////////////////
2173
2174std::string TClassEdit::GetNameForIO(const std::string& templateInstanceName,
2176 bool* hasChanged)
2177{
2178 // Decompose template name into pieces and remount it applying the necessary
2179 // transformations necessary for the ROOT IO subsystem, namely:
2180 // - Transform std::unique_ptr<T> into T (for selections) (also nested)
2181 // - Transform std::unique_ptr<const T> into T (for selections) (also nested)
2182 // - Transform std::COLL<std::unique_ptr<T>> into std::COLL<T*> (also nested)
2183 // Name normalisation is respected (e.g. spaces).
2184 // The implementation uses an internal class defined in the cxx file.
2186 auto nameForIO = node.ToString();
2187 if (hasChanged) {
2188 *hasChanged = node.HasChanged();
2189 }
2190 return nameForIO;
2191}
2192
2193////////////////////////////////////////////////////////////////////////////////
2194// We could introduce a tuple as return type, but we be consistent with the rest
2195// of the code.
2196bool TClassEdit::GetStdArrayProperties(const char* typeName,
2197 std::string& typeNameBuf,
2198 std::array<int, 5>& maxIndices,
2199 int& ndim)
2200{
2201 if (!IsStdArray(typeName)) return false;
2202
2203 // We have an array, it's worth continuing
2204 NameCleanerForIO node(typeName);
2205
2206 // We now recurse updating the data according to what we find
2207 auto childNodes = node.GetChildNodes();
2208 for (ndim = 1;ndim <=5 ; ndim++) {
2209 maxIndices[ndim-1] = std::atoi(childNodes->back()->GetName().c_str());
2210 auto& frontNode = childNodes->front();
2211 typeNameBuf = frontNode->GetName();
2212 if (! IsStdArray(typeNameBuf+"<")) {
2213 typeNameBuf = frontNode->ToString();
2214 return true;
2215 }
2216 childNodes = frontNode->GetChildNodes();
2217 }
2218
2219 return true;
2220}
2221
2222
2223////////////////////////////////////////////////////////////////////////////////
2224/// Demangle in a portable way the type id name.
2225/// IMPORTANT: The caller is responsible for freeing the returned const char*
2226
2227char* TClassEdit::DemangleTypeIdName(const std::type_info& ti, int& errorCode)
2228{
2229 const char* mangled_name = ti.name();
2231}
2232/*
2233/// Result of splitting a function declaration into
2234/// fReturnType fScopeName::fFunctionName<fFunctionTemplateArguments>(fFunctionParameters)
2235struct FunctionSplitInfo {
2236 /// Return type of the function, might be empty if the function declaration string did not provide it.
2237 std::string fReturnType;
2238
2239 /// Name of the scope qualification of the function, possibly empty
2240 std::string fScopeName;
2241
2242 /// Name of the function
2243 std::string fFunctionName;
2244
2245 /// Template arguments of the function template specialization, if any; will contain one element "" for
2246 /// `function<>()`
2247 std::vector<std::string> fFunctionTemplateArguments;
2248
2249 /// Function parameters.
2250 std::vector<std::string> fFunctionParameters;
2251};
2252*/
2253
2254namespace {
2255 /// Find the first occurrence of any of needle's characters in haystack that
2256 /// is not nested in a <>, () or [] pair.
2257 std::size_t FindNonNestedNeedles(std::string_view haystack, string_view needles)
2258 {
2259 std::stack<char> expected;
2260 for (std::size_t pos = 0, end = haystack.length(); pos < end; ++pos) {
2261 char c = haystack[pos];
2262 if (expected.empty()) {
2263 if (needles.find(c) != std::string_view::npos)
2264 return pos;
2265 } else {
2266 if (c == expected.top()) {
2267 expected.pop();
2268 continue;
2269 }
2270 }
2271 switch (c) {
2272 case '<': expected.emplace('>'); break;
2273 case '(': expected.emplace(')'); break;
2274 case '[': expected.emplace(']'); break;
2275 }
2276 }
2277 return std::string_view::npos;
2278 }
2279
2280 /// Find the first occurrence of `::` that is not nested in a <>, () or [] pair.
2281 std::size_t FindNonNestedDoubleColons(std::string_view haystack)
2282 {
2283 std::size_t lenHaystack = haystack.length();
2284 std::size_t prevAfterColumn = 0;
2285 while (true) {
2286 std::size_t posColumn = FindNonNestedNeedles(haystack.substr(prevAfterColumn), ":");
2287 if (posColumn == std::string_view::npos)
2288 return std::string_view::npos;
2290 // prevAfterColumn must have "::", i.e. two characters:
2291 if (prevAfterColumn + 1 >= lenHaystack)
2292 return std::string_view::npos;
2293
2294 ++prevAfterColumn; // done with first (or only) ':'
2295 if (haystack[prevAfterColumn] == ':')
2296 return prevAfterColumn - 1;
2297 ++prevAfterColumn; // That was not a ':'.
2298 }
2299
2300 return std::string_view::npos;
2301 }
2302
2303 std::string_view StripSurroundingSpace(std::string_view str)
2304 {
2305 while (!str.empty() && std::isspace(str[0]))
2306 str.remove_prefix(1);
2307 while (!str.empty() && std::isspace(str.back()))
2308 str.remove_suffix(1);
2309 return str;
2310 }
2311
2312 std::string ToString(std::string_view sv)
2313 {
2314 // ROOT's string_view backport doesn't add the new std::string contructor and assignment;
2315 // convert to std::string instead and assign that.
2316 return std::string(sv.data(), sv.length());
2317 }
2318} // unnamed namespace
2319
2320/// Split a function declaration into its different parts.
2322{
2323 // General structure:
2324 // `...` last-space `...` (`...`)
2325 // The first `...` is the return type.
2326 // The second `...` is the (possibly scoped) function name.
2327 // The third `...` are the parameters.
2328 // The function name can be of the form `...`<`...`>
2329 std::size_t posArgs = FindNonNestedNeedles(decl, "(");
2330 std::string_view declNoArgs = decl.substr(0, posArgs);
2331
2332 std::size_t prevAfterWhiteSpace = 0;
2333 static const char whitespace[] = " \t\n";
2334 while (declNoArgs.length() > prevAfterWhiteSpace) {
2336 if (posWS == std::string_view::npos)
2337 break;
2339 while (declNoArgs.length() > prevAfterWhiteSpace
2342 }
2343
2344 /// Include any '&*' in the return type:
2345 std::size_t endReturn = prevAfterWhiteSpace;
2346 while (declNoArgs.length() > endReturn
2347 && strchr("&* \t \n", declNoArgs[endReturn]))
2348 ++endReturn;
2349
2350 result.fReturnType = ToString(StripSurroundingSpace(declNoArgs.substr(0, endReturn)));
2351
2352 /// scope::anotherscope::functionName<tmplt>:
2353 std::string_view scopeFunctionTmplt = declNoArgs.substr(endReturn);
2355 while (prevAtScope != std::string_view::npos
2356 && scopeFunctionTmplt.length() > prevAtScope + 2) {
2358 if (posScope == std::string_view::npos)
2359 break;
2360 prevAtScope += posScope + 2;
2361 }
2362
2363 std::size_t afterScope = prevAtScope + 2;
2364 if (prevAtScope == std::string_view::npos) {
2365 afterScope = 0;
2366 prevAtScope = 0;
2367 }
2368
2369 result.fScopeName = ToString(StripSurroundingSpace(scopeFunctionTmplt.substr(0, prevAtScope)));
2370 std::string_view funcNameTmplArgs = scopeFunctionTmplt.substr(afterScope);
2371
2372 result.fFunctionTemplateArguments.clear();
2374 if (posTmpltOpen != std::string_view::npos) {
2375 result.fFunctionName = ToString(StripSurroundingSpace(funcNameTmplArgs.substr(0, posTmpltOpen)));
2376
2377 // Parse template parameters:
2378 std::string_view tmpltArgs = funcNameTmplArgs.substr(posTmpltOpen + 1);
2379 std::size_t posTmpltClose = FindNonNestedNeedles(tmpltArgs, ">");
2380 if (posTmpltClose != std::string_view::npos) {
2381 tmpltArgs = tmpltArgs.substr(0, posTmpltClose);
2382 std::size_t prevAfterArg = 0;
2383 while (tmpltArgs.length() > prevAfterArg) {
2384 std::size_t posComma = FindNonNestedNeedles(tmpltArgs.substr(prevAfterArg), ",");
2385 if (posComma == std::string_view::npos) {
2386 break;
2387 }
2388 result.fFunctionTemplateArguments.emplace_back(ToString(StripSurroundingSpace(tmpltArgs.substr(prevAfterArg, posComma))));
2389 prevAfterArg += posComma + 1;
2390 }
2391 // Add the trailing arg.
2392 result.fFunctionTemplateArguments.emplace_back(ToString(StripSurroundingSpace(tmpltArgs.substr(prevAfterArg))));
2393 }
2394 } else {
2395 result.fFunctionName = ToString(StripSurroundingSpace(funcNameTmplArgs));
2396 }
2397
2398 result.fFunctionParameters.clear();
2399 if (posArgs != std::string_view::npos) {
2400 /// (params)
2401 std::string_view params = decl.substr(posArgs + 1);
2402 std::size_t posEndArgs = FindNonNestedNeedles(params, ")");
2403 if (posEndArgs != std::string_view::npos) {
2404 params = params.substr(0, posEndArgs);
2405 std::size_t prevAfterArg = 0;
2406 while (params.length() > prevAfterArg) {
2407 std::size_t posComma = FindNonNestedNeedles(params.substr(prevAfterArg), ",");
2408 if (posComma == std::string_view::npos) {
2409 result.fFunctionParameters.emplace_back(ToString(StripSurroundingSpace(params.substr(prevAfterArg))));
2410 break;
2411 }
2412 result.fFunctionParameters.emplace_back(ToString(StripSurroundingSpace(params.substr(prevAfterArg, posComma))));
2413 prevAfterArg += posComma + 1; // skip ','
2414 }
2415 }
2416 }
2417
2418 return true;
2419}
#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:159
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:229
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:230
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:254
bool IsArtificial(std::string_view name)
Definition TClassEdit.h:205
bool GetStdArrayProperties(const char *typeName, std::string &typeNameBuf, std::array< int, 5 > &maxIndices, int &ndim)
std::string GetNameForIO(const std::string &templateInstanceName, TClassEdit::EModType mode=TClassEdit::kNone, bool *hasChanged=nullptr)
int STLArgs(int kind)
Return number of arguments for STL container before allocator.
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:228
@ 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:286
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:142
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()