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