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