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