Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RFieldUtils.cxx
Go to the documentation of this file.
1/// \file RFieldUtils.cxx
2/// \ingroup NTuple
3/// \author Jonas Hahnfeld <jonas.hahnfeld@cern.ch>
4/// \date 2024-11-19
5
7
8#include <ROOT/RField.hxx>
9#include <ROOT/RLogger.hxx>
11#include <ROOT/RNTupleTypes.hxx>
12#include <ROOT/RNTupleUtils.hxx>
13
14#include <TClass.h>
15#include <TClassEdit.h>
16#include <TDictAttributeMap.h>
17
18#include <algorithm>
19#include <charconv>
20#include <limits>
21#include <string>
22#include <string_view>
23#include <system_error>
24#include <unordered_map>
25#include <utility>
26#include <vector>
27
28namespace {
29
30std::string GetRenormalizedDemangledTypeName(const std::string &demangledName, bool renormalizeStdString);
31
32const std::unordered_map<std::string_view, std::string_view> typeTranslationMap{
33 {"Bool_t", "bool"},
34 {"Float_t", "float"},
35 {"Double_t", "double"},
36 {"string", "std::string"},
37
38 {"byte", "std::byte"},
39 {"Char_t", "char"},
40 {"int8_t", "std::int8_t"},
41 {"UChar_t", "unsigned char"},
42 {"uint8_t", "std::uint8_t"},
43
44 {"Short_t", "short"},
45 {"int16_t", "std::int16_t"},
46 {"UShort_t", "unsigned short"},
47 {"uint16_t", "std::uint16_t"},
48
49 {"Int_t", "int"},
50 {"int32_t", "std::int32_t"},
51 {"UInt_t", "unsigned int"},
52 {"unsigned", "unsigned int"},
53 {"uint32_t", "std::uint32_t"},
54
55 // Long_t and ULong_t follow the platform's size of long and unsigned long: They are 64 bit on 64-bit Linux and
56 // macOS, but 32 bit on 32-bit platforms and Windows (regardless of pointer size).
57 {"Long_t", "long"},
58 {"ULong_t", "unsigned long"},
59
60 {"Long64_t", "long long"},
61 {"int64_t", "std::int64_t"},
62 {"ULong64_t", "unsigned long long"},
63 {"uint64_t", "std::uint64_t"}};
64
65// Natively supported types drop the default template arguments and the CV qualifiers in template arguments.
66bool IsUserClass(const std::string &typeName)
67{
68 return typeName.rfind("std::", 0) != 0 && typeName.rfind("ROOT::VecOps::RVec<", 0) != 0;
69}
70
71/// Parse a type name of the form `T[n][m]...` and return the base type `T` and a vector that contains,
72/// in order, the declared size for each dimension, e.g. for `unsigned char[1][2][3]` it returns the tuple
73/// `{"unsigned char", {1, 2, 3}}`. Extra whitespace in `typeName` should be removed before calling this function.
74///
75/// If `typeName` is not an array type, it returns a tuple `{T, {}}`. On error, it returns a default-constructed tuple.
76std::tuple<std::string, std::vector<std::size_t>> ParseArrayType(const std::string &typeName)
77{
78 std::vector<std::size_t> sizeVec;
79
80 // Only parse outer array definition, i.e. the right `]` should be at the end of the type name
81 std::string prefix{typeName};
82 while (prefix.back() == ']') {
83 auto posRBrace = prefix.size() - 1;
84 auto posLBrace = prefix.rfind('[', posRBrace);
85 if (posLBrace == std::string_view::npos) {
86 throw ROOT::RException(R__FAIL(std::string("invalid array type: ") + typeName));
87 }
88 if (posRBrace - posLBrace <= 1) {
89 throw ROOT::RException(R__FAIL(std::string("invalid array type: ") + typeName));
90 }
91
92 const std::size_t size =
94 if (size == 0) {
95 throw ROOT::RException(R__FAIL(std::string("invalid array size: ") + typeName));
96 }
97
98 sizeVec.insert(sizeVec.begin(), size);
99 prefix.resize(posLBrace);
100 }
101 return std::make_tuple(prefix, sizeVec);
102}
103
104/// Assembles a (nested) std::array<> based type based on the dimensions retrieved from ParseArrayType(). Returns
105/// baseType if there are no dimensions.
106std::string GetStandardArrayType(const std::string &baseType, const std::vector<std::size_t> &dimensions)
107{
108 std::string typeName = baseType;
109 for (auto i = dimensions.rbegin(), iEnd = dimensions.rend(); i != iEnd; ++i) {
110 typeName = "std::array<" + typeName + "," + std::to_string(*i) + ">";
111 }
112 return typeName;
113}
114
115// Recursively normalizes a template argument using the regular type name normalizer F as a helper.
116template <typename F>
117std::string GetNormalizedTemplateArg(const std::string &arg, bool keepQualifier, F fnTypeNormalizer)
118{
119 R__ASSERT(!arg.empty());
120
121 if (std::isdigit(arg[0]) || arg[0] == '-') {
122 // Integer template argument
124 }
125
126 if (!keepQualifier)
127 return fnTypeNormalizer(arg);
128
129 std::string qualifier;
130 // Type name template argument; template arguments must keep their CV qualifier. We assume that fnTypeNormalizer
131 // strips the qualifier.
132 // Demangled names may have the CV qualifiers suffixed and not prefixed (but const always before volatile).
133 // Note that in the latter case, we may have the CV qualifiers before array brackets, e.g. `int const[2]`.
134 const auto [base, _] = ParseArrayType(arg);
135 if (base.rfind("const ", 0) == 0 || base.rfind("volatile const ", 0) == 0 ||
136 base.find(" const", base.length() - 6) != std::string::npos ||
137 base.find(" const volatile", base.length() - 15) != std::string::npos) {
138 qualifier += "const ";
139 }
140 if (base.rfind("volatile ", 0) == 0 || base.rfind("const volatile ", 0) == 0 ||
141 base.find(" volatile", base.length() - 9) != std::string::npos) {
142 qualifier += "volatile ";
143 }
144 return qualifier + fnTypeNormalizer(arg);
145}
146
147using AnglePos = std::pair<std::string::size_type, std::string::size_type>;
148std::vector<AnglePos> FindTemplateAngleBrackets(const std::string &typeName)
149{
150 std::vector<AnglePos> result;
151 std::string::size_type currentPos = 0;
152 while (currentPos < typeName.size()) {
153 const auto posOpen = typeName.find('<', currentPos);
154 if (posOpen == std::string::npos) {
155 // If there are no more templates, the function is done.
156 break;
157 }
158
159 auto posClose = posOpen + 1;
160 int level = 1;
161 while (posClose < typeName.size()) {
162 const auto c = typeName[posClose];
163 if (c == '<') {
164 level++;
165 } else if (c == '>') {
166 if (level == 1) {
167 break;
168 }
169 level--;
170 }
171 posClose++;
172 }
173 // We should have found a closing angle bracket at the right level.
174 R__ASSERT(posClose < typeName.size());
175 result.emplace_back(posOpen, posClose);
176
177 // If we are not at the end yet, the following two characeters should be :: for nested types.
178 if (posClose < typeName.size() - 1) {
179 R__ASSERT(typeName.substr(posClose + 1, 2) == "::");
180 }
181 currentPos = posClose + 1;
182 }
183
184 return result;
185}
186
187// TClassEdit::CleanType and the name demangling insert blanks between closing angle brackets,
188// as they were required before C++11. Name demangling introduces a blank before array dimensions,
189// which should also be removed.
190void RemoveSpaceBefore(std::string &typeName, char beforeChar)
191{
192 auto dst = typeName.begin();
193 auto end = typeName.end();
194 for (auto src = dst; src != end; ++src) {
195 if (*src == ' ') {
196 auto next = src + 1;
197 if (next != end && *next == beforeChar) {
198 // Skip this space before a closing angle bracket.
199 continue;
200 }
201 }
202 *(dst++) = *src;
203 }
204 typeName.erase(dst, end);
205}
206
207// The demangled name adds spaces after commas
208void RemoveSpaceAfter(std::string &typeName, char afterChar)
209{
210 auto dst = typeName.begin();
211 auto end = typeName.end();
212 for (auto src = dst; src != end; ++src) {
213 *(dst++) = *src;
214 if (*src == afterChar) {
215 auto next = src + 1;
216 if (next != end && *next == ' ') {
217 // Skip this space before a closing angle bracket.
218 ++src;
219 }
220 }
221 }
222 typeName.erase(dst, end);
223}
224
225// We normalize typenames to omit any `class`, `struct`, `enum` prefix
226void RemoveLeadingKeyword(std::string &typeName)
227{
228 if (typeName.rfind("class ", 0) == 0) {
229 typeName.erase(0, 6);
230 } else if (typeName.rfind("struct ", 0) == 0) {
231 typeName.erase(0, 7);
232 } else if (typeName.rfind("enum ", 0) == 0) {
233 typeName.erase(0, 5);
234 }
235}
236
237// Needed for template arguments in demangled names
238void RemoveCVQualifiers(std::string &typeName)
239{
240 if (typeName.rfind("const ", 0) == 0)
241 typeName.erase(0, 6);
242 if (typeName.rfind("volatile ", 0) == 0)
243 typeName.erase(0, 9);
244 if (typeName.find(" volatile", typeName.length() - 9) != std::string::npos)
245 typeName.erase(typeName.length() - 9);
246 if (typeName.find(" const", typeName.length() - 6) != std::string::npos)
247 typeName.erase(typeName.length() - 6);
248}
249
250// Map fundamental integer types to stdint integer types (e.g. int --> std::int32_t)
251void MapIntegerType(std::string &typeName)
252{
253 if (typeName == "signed char") {
255 } else if (typeName == "unsigned char") {
257 } else if (typeName == "short" || typeName == "short int" || typeName == "signed short" ||
258 typeName == "signed short int") {
260 } else if (typeName == "unsigned short" || typeName == "unsigned short int") {
262 } else if (typeName == "int" || typeName == "signed" || typeName == "signed int") {
263 typeName = ROOT::RField<int>::TypeName();
264 } else if (typeName == "unsigned" || typeName == "unsigned int") {
266 } else if (typeName == "long" || typeName == "long int" || typeName == "signed long" ||
267 typeName == "signed long int") {
269 } else if (typeName == "unsigned long" || typeName == "unsigned long int") {
271 } else if (typeName == "long long" || typeName == "long long int" || typeName == "signed long long" ||
272 typeName == "signed long long int") {
274 } else if (typeName == "unsigned long long" || typeName == "unsigned long long int") {
276 } else {
277 // The following two types are 64-bit integers on Windows that we can encounter during renormalization of
278 // demangled std::type_info names.
279 if (typeName == "__int64") {
280 typeName = "std::int64_t";
281 } else if (typeName == "unsigned __int64") {
282 typeName = "std::uint64_t";
283 }
284 }
285}
286
287// Note that ROOT Meta already defines GetDemangledTypeName(), which does both demangling and normalizing.
288std::string GetRawDemangledTypeName(const std::type_info &ti)
289{
290 int e;
291 char *str = TClassEdit::DemangleName(ti.name(), e);
292 R__ASSERT(str && e == 0);
293 std::string result{str};
294 free(str);
295
296 return result;
297}
298
299// Reverse std::string --> std::basic_string<char> from the demangling
301{
302 static const std::string gStringName =
303 GetRenormalizedDemangledTypeName(GetRawDemangledTypeName(typeid(std::string)), false /* renormalizeStdString */);
304
305 // For real nested types of std::string (not typedefs like std::string::size_type), we would need to also check
306 // something like (normalizedTypeName + "::" == gStringName + "::") and replace only the prefix. However, since
307 // such a nested type is not standardized, it currently does not seem necessary to add the logic.
309 normalizedTypeName = "std::string";
310 }
311}
312
313// Reverse "internal" namespace prefix found in demangled names, such as std::vector<T> --> std::__1::vector<T>
315{
316 static std::vector<std::pair<std::string, std::string>> gDistortedStdlibNames = []() {
317 // clang-format off
318 // Listed in order of appearance in the BinaryFormatSpecification.md
319 static const std::vector<std::pair<const std::type_info &, std::string>> gCandidates =
320 {{typeid(std::vector<char>), "std::vector<"},
321 {typeid(std::array<char, 1>), "std::array<"},
322 {typeid(std::variant<char>), "std::variant<"},
323 {typeid(std::pair<char, char>), "std::pair<"},
324 {typeid(std::tuple<char>), "std::tuple<"},
325 {typeid(std::bitset<1>), "std::bitset<"},
326 {typeid(std::unique_ptr<char>), "std::unique_ptr<"},
327 {typeid(std::optional<char>), "std::optional<"},
328 {typeid(std::set<char>), "std::set<"},
329 {typeid(std::unordered_set<char>), "std::unordered_set<"},
330 {typeid(std::multiset<char>), "std::multiset<"},
331 {typeid(std::unordered_multiset<char>), "std::unordered_multiset<"},
332 {typeid(std::map<char, char>), "std::map<"},
333 {typeid(std::unordered_map<char, char>), "std::unordered_map<"},
334 {typeid(std::multimap<char, char>), "std::multimap<"},
335 {typeid(std::unordered_multimap<char, char>), "std::unordered_multimap<"},
336 {typeid(std::atomic<char>), "std::atomic<"}};
337 // clang-format on
338
339 std::vector<std::pair<std::string, std::string>> result;
340 for (const auto &[ti, prefix] : gCandidates) {
341 const auto dm = GetRawDemangledTypeName(ti);
342 if (dm.rfind(prefix, 0) == std::string::npos)
343 result.push_back(std::make_pair(dm.substr(0, dm.find('<') + 1), prefix));
344 }
345
346 return result;
347 }();
348
349 for (const auto &[seenPrefix, canonicalPrefix] : gDistortedStdlibNames) {
350 if (normalizedTypeName.rfind(seenPrefix, 0) == 0) {
352 break;
353 }
354 }
355}
356
357template <typename F>
359{
361 R__ASSERT(!angleBrackets.empty());
362
363 std::string normName;
364 std::string::size_type currentPos = 0;
365 for (std::size_t i = 0; i < angleBrackets.size(); i++) {
366 const auto [posOpen, posClose] = angleBrackets[i];
367 // Append the type prefix until the open angle bracket.
369
370 const auto argList = templatedTypeName.substr(posOpen + 1, posClose - posOpen - 1);
372 R__ASSERT(!templateArgs.empty());
373
375 for (const auto &a : templateArgs) {
377 }
378
379 normName[normName.size() - 1] = '>';
380 currentPos = posClose + 1;
381 }
382
383 // Append the rest of the type from the last closing angle bracket.
384 const auto lastClosePos = angleBrackets.back().second;
386
388}
389
390// Given a type name normalized by ROOT Meta, return the type name normalized according to the RNTuple rules.
391std::string GetRenormalizedMetaTypeName(const std::string &metaNormalizedName)
392{
394 // RNTuple resolves Double32_t for the normalized type name but keeps Double32_t for the type alias
395 // (also in template parameters)
396 if (canonicalTypePrefix == "Double32_t")
397 return "double";
398
399 if (canonicalTypePrefix.find('<') == std::string::npos) {
400 // If there are no templates, the function is done.
401 return canonicalTypePrefix;
402 }
403
404 std::string normName{canonicalTypePrefix};
406
407 return normName;
408}
409
410// Given a demangled name ("normalized by the compiler"), return the type name normalized according to the
411// RNTuple rules.
412std::string GetRenormalizedDemangledTypeName(const std::string &demangledName, bool renormalizeStdString)
413{
414 std::string tn{demangledName};
415 RemoveSpaceBefore(tn, '[');
420
421 if (canonicalTypePrefix.find('<') == std::string::npos) {
422 // If there are no templates, the function is done.
424 }
427 RemoveSpaceBefore(canonicalTypePrefix, ','); // MSVC fancies spaces before commas in the demangled name
429
430 // Remove optional stdlib template arguments
431 int maxTemplateArgs = 0;
432 if (canonicalTypePrefix.rfind("std::vector<", 0) == 0 || canonicalTypePrefix.rfind("std::set<", 0) == 0 ||
433 canonicalTypePrefix.rfind("std::unordered_set<", 0) == 0 ||
434 canonicalTypePrefix.rfind("std::multiset<", 0) == 0 ||
435 canonicalTypePrefix.rfind("std::unordered_multiset<", 0) == 0 ||
436 canonicalTypePrefix.rfind("std::unique_ptr<", 0) == 0) {
437 maxTemplateArgs = 1;
438 } else if (canonicalTypePrefix.rfind("std::map<", 0) == 0 ||
439 canonicalTypePrefix.rfind("std::unordered_map<", 0) == 0 ||
440 canonicalTypePrefix.rfind("std::multimap<", 0) == 0 ||
441 canonicalTypePrefix.rfind("std::unordered_multimap<", 0) == 0) {
442 maxTemplateArgs = 2;
443 }
444
445 std::string normName{canonicalTypePrefix};
448 });
449 // In RenormalizeStdString(), we normalize the demangled type name of `std::string`,
450 // so we need to prevent an endless recursion.
453 }
454
456}
457
458} // namespace
459
460std::string ROOT::Internal::GetCanonicalTypePrefix(const std::string &typeName)
461{
462 // Remove outer cv qualifiers and extra white spaces
463 const std::string cleanedType = TClassEdit::CleanType(typeName.c_str(), /*mode=*/1);
464
465 // Can happen when called from RFieldBase::Create() and is caught there
466 if (cleanedType.empty())
467 return "";
468
470
472 if (canonicalType.substr(0, 2) == "::") {
473 canonicalType.erase(0, 2);
474 }
475
477
478 if (canonicalType.substr(0, 6) == "array<") {
479 canonicalType = "std::" + canonicalType;
480 } else if (canonicalType.substr(0, 7) == "atomic<") {
481 canonicalType = "std::" + canonicalType;
482 } else if (canonicalType.substr(0, 7) == "bitset<") {
483 canonicalType = "std::" + canonicalType;
484 } else if (canonicalType.substr(0, 4) == "map<") {
485 canonicalType = "std::" + canonicalType;
486 } else if (canonicalType.substr(0, 9) == "multimap<") {
487 canonicalType = "std::" + canonicalType;
488 } else if (canonicalType.substr(0, 9) == "multiset<") {
489 canonicalType = "std::" + canonicalType;
490 }
491 if (canonicalType.substr(0, 5) == "pair<") {
492 canonicalType = "std::" + canonicalType;
493 } else if (canonicalType.substr(0, 4) == "set<") {
494 canonicalType = "std::" + canonicalType;
495 } else if (canonicalType.substr(0, 6) == "tuple<") {
496 canonicalType = "std::" + canonicalType;
497 } else if (canonicalType.substr(0, 11) == "unique_ptr<") {
498 canonicalType = "std::" + canonicalType;
499 } else if (canonicalType.substr(0, 14) == "unordered_map<") {
500 canonicalType = "std::" + canonicalType;
501 } else if (canonicalType.substr(0, 19) == "unordered_multimap<") {
502 canonicalType = "std::" + canonicalType;
503 } else if (canonicalType.substr(0, 19) == "unordered_multiset<") {
504 canonicalType = "std::" + canonicalType;
505 } else if (canonicalType.substr(0, 14) == "unordered_set<") {
506 canonicalType = "std::" + canonicalType;
507 } else if (canonicalType.substr(0, 8) == "variant<") {
508 canonicalType = "std::" + canonicalType;
509 } else if (canonicalType.substr(0, 7) == "vector<") {
510 canonicalType = "std::" + canonicalType;
511 } else if (canonicalType.substr(0, 11) == "ROOT::RVec<") {
512 canonicalType = "ROOT::VecOps::RVec<" + canonicalType.substr(11);
513 }
514
515 if (auto it = typeTranslationMap.find(canonicalType); it != typeTranslationMap.end()) {
516 canonicalType = it->second;
517 }
518
520
522}
523
524std::string ROOT::Internal::GetRenormalizedTypeName(const std::type_info &ti)
525{
526 return GetRenormalizedDemangledTypeName(GetRawDemangledTypeName(ti), true /* renormalizeStdString */);
527}
528
533
535{
539 std::string shortType;
540 splitname.ShortType(shortType, modType);
542
543 if (canonicalTypePrefix.find('<') == std::string::npos) {
544 // If there are no templates, the function is done.
545 return canonicalTypePrefix;
546 }
547
549 R__ASSERT(!angleBrackets.empty());
550
551 // For user-defined class types, we will need to get the default-initialized template arguments.
553
554 std::string normName;
555 std::string::size_type currentPos = 0;
556 for (std::size_t i = 0; i < angleBrackets.size(); i++) {
557 const auto [posOpen, posClose] = angleBrackets[i];
558 // Append the type prefix until the open angle bracket.
560
561 const auto argList = canonicalTypePrefix.substr(posOpen + 1, posClose - posOpen - 1);
562 const auto templateArgs = TokenizeTypeList(argList);
563 R__ASSERT(!templateArgs.empty());
564
565 for (const auto &a : templateArgs) {
567 }
568
569 // For user-defined classes, append default-initialized template arguments.
570 if (isUserClass) {
571 const auto cl = TClass::GetClass(canonicalTypePrefix.substr(0, posClose + 1).c_str());
572 if (cl) {
573 const std::string expandedName = cl->GetName();
575 // We can have fewer pairs than angleBrackets, for example in case of type aliases.
577
579 const auto expandedArgList =
582 // Note that we may be in a sitation where expandedTemplateArgs.size() is _smaller_ than
583 // templateArgs.size(), which is when the input type name has the optional template arguments explicitly
584 // spelled out but ROOT Meta is told to ignore some template arguments.
585
586 for (std::size_t j = templateArgs.size(); j < expandedTemplateArgs.size(); ++j) {
587 normName +=
589 }
590 }
591 }
592
593 normName[normName.size() - 1] = '>';
594 currentPos = posClose + 1;
595 }
596
597 // Append the rest of the type from the last closing angle bracket.
598 const auto lastClosePos = angleBrackets.back().second;
600
601 return normName;
602}
603
604std::string ROOT::Internal::GetNormalizedInteger(long long val)
605{
606 return std::to_string(val);
607}
608
609std::string ROOT::Internal::GetNormalizedInteger(unsigned long long val)
610{
611 if (val > std::numeric_limits<std::int64_t>::max())
612 return std::to_string(val) + "u";
613 return std::to_string(val);
614}
615
623
624long long ROOT::Internal::ParseIntTypeToken(const std::string &intToken)
625{
626 std::size_t nChars = 0;
627 long long res = std::stoll(intToken, &nChars);
628 if (nChars == intToken.size())
629 return res;
630
631 assert(nChars < intToken.size());
632 if (nChars == 0) {
633 throw RException(R__FAIL("invalid integer type token: " + intToken));
634 }
635
636 auto suffix = intToken.substr(nChars);
637 std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::toupper);
638 if (suffix == "L" || suffix == "LL")
639 return res;
640 if (res >= 0 && (suffix == "U" || suffix == "UL" || suffix == "ULL"))
641 return res;
642
643 throw RException(R__FAIL("invalid integer type token: " + intToken));
644}
645
646unsigned long long ROOT::Internal::ParseUIntTypeToken(const std::string &uintToken)
647{
648 std::size_t nChars = 0;
649 unsigned long long res = std::stoull(uintToken, &nChars);
650 if (nChars == uintToken.size())
651 return res;
652
653 assert(nChars < uintToken.size());
654 if (nChars == 0) {
655 throw RException(R__FAIL("invalid integer type token: " + uintToken));
656 }
657
658 auto suffix = uintToken.substr(nChars);
659 std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::toupper);
660 if (suffix == "U" || suffix == "L" || suffix == "LL" || suffix == "UL" || suffix == "ULL")
661 return res;
662
663 throw RException(R__FAIL("invalid integer type token: " + uintToken));
664}
665
667{
668 auto am = cl->GetAttributeMap();
669 if (!am || !am->HasKey("rntuple.streamerMode"))
670 return ERNTupleSerializationMode::kUnset;
671
672 std::string value = am->GetPropertyAsString("rntuple.streamerMode");
673 std::transform(value.begin(), value.end(), value.begin(), ::toupper);
674 if (value == "TRUE") {
675 return ERNTupleSerializationMode::kForceStreamerMode;
676 } else if (value == "FALSE") {
677 return ERNTupleSerializationMode::kForceNativeMode;
678 } else {
679 R__LOG_WARNING(ROOT::Internal::NTupleLog()) << "invalid setting for 'rntuple.streamerMode' class attribute: "
680 << am->GetPropertyAsString("rntuple.streamerMode");
681 return ERNTupleSerializationMode::kUnset;
682 }
683}
684
685std::vector<std::string> ROOT::Internal::TokenizeTypeList(std::string_view templateType, std::size_t maxArgs)
686{
687 std::vector<std::string> result;
688 if (templateType.empty())
689 return result;
690
691 const char *eol = templateType.data() + templateType.length();
692 const char *typeBegin = templateType.data();
693 const char *typeCursor = templateType.data();
694 unsigned int nestingLevel = 0;
695 while (typeCursor != eol) {
696 switch (*typeCursor) {
697 case '<': ++nestingLevel; break;
698 case '>': --nestingLevel; break;
699 case ',':
700 if (nestingLevel == 0) {
701 result.push_back(std::string(typeBegin, typeCursor - typeBegin));
702 if (maxArgs && result.size() == maxArgs)
703 return result;
704 typeBegin = typeCursor + 1;
705 }
706 break;
707 }
708 typeCursor++;
709 }
710 result.push_back(std::string(typeBegin, typeCursor - typeBegin));
711 return result;
712}
713
715 const std::type_info &ti)
716{
717 // Fast path: the caller provided the expected type name (from RField<T>::TypeName())
719 return true;
720
721 // The type name may be equal to the alternative, short type name issued by Meta. This is a rare case used, e.g.,
722 // by the ATLAS DataVector class to hide a default template parameter from the on-disk type name.
723 // Thus, we check again using first ROOT Meta normalization followed by RNTuple re-normalization.
725}
726
728{
729 // Information to print in a single line of the type trace
730 struct RFieldInfo {
731 std::string fFieldName;
732 std::string fTypeName;
734 std::uint32_t fTypeVersion = 0;
735 std::optional<std::uint32_t> fTypeChecksum;
736 };
737
738 std::vector<const RFieldBase *> inMemoryStack;
739 std::vector<const RFieldDescriptor *> onDiskStack;
740
741 auto fnGetLine = [](const RFieldInfo &fieldInfo, int level) -> std::string {
742 std::string line = std::string(2 * level, ' ') + fieldInfo.fFieldName + " [" + fieldInfo.fTypeName;
743 if (fieldInfo.fTypeVersion > 0)
744 line += ", type version: " + std::to_string(fieldInfo.fTypeVersion);
745 if (fieldInfo.fTypeChecksum)
746 line += ", type checksum: " + std::to_string(*fieldInfo.fTypeChecksum);
747 line += "] (id: " + std::to_string(fieldInfo.fFieldId) + ")\n";
748 return line;
749 };
750
751 const RFieldBase *fieldPtr = &field;
752 while (fieldPtr->GetParent()) {
753 inMemoryStack.push_back(fieldPtr);
754 fieldPtr = fieldPtr->GetParent();
755 }
756
757 auto fieldId = field.GetOnDiskId();
758 while (fieldId != kInvalidDescriptorId && fieldId != desc.GetFieldZeroId()) {
759 const auto &fieldDesc = desc.GetFieldDescriptor(fieldId);
760 onDiskStack.push_back(&fieldDesc);
761 fieldId = fieldDesc.GetParentId();
762 }
763
764 std::string report = "In-memory field/type hierarchy:\n";
765 int indentLevel = 0;
766 for (auto itr = inMemoryStack.rbegin(); itr != inMemoryStack.rend(); ++itr, ++indentLevel) {
767 RFieldInfo fieldInfo;
768 fieldInfo.fFieldName = (*itr)->GetFieldName();
769 fieldInfo.fTypeName = (*itr)->GetTypeName();
770 fieldInfo.fFieldId = (*itr)->GetOnDiskId();
771 fieldInfo.fTypeVersion = (*itr)->GetTypeVersion();
772 if ((*itr)->GetTraits() & RFieldBase::kTraitTypeChecksum)
773 fieldInfo.fTypeChecksum = (*itr)->GetTypeChecksum();
774
776 }
777
778 report += "On-disk field/type hierarchy:\n";
779 indentLevel = 0;
780 for (auto itr = onDiskStack.rbegin(); itr != onDiskStack.rend(); ++itr, ++indentLevel) {
781 RFieldInfo fieldInfo;
782 fieldInfo.fFieldName = (*itr)->GetFieldName();
783 fieldInfo.fTypeName = (*itr)->GetTypeName();
784 fieldInfo.fFieldId = (*itr)->GetId();
785 fieldInfo.fTypeVersion = (*itr)->GetTypeVersion();
786 fieldInfo.fTypeChecksum = (*itr)->GetTypeChecksum();
787
789 }
790
791 return report;
792}
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:300
#define R__LOG_WARNING(...)
Definition RLogger.hxx:358
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
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 value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t src
#define _(A, B)
Definition cfortran.h:108
#define free
Definition civetweb.c:1578
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
A field translates read and write calls from/to underlying columns to/from tree values.
static std::string TypeName()
Definition RField.hxx:315
The on-storage metadata of an RNTuple.
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
ROOT::DescriptorId_t GetFieldZeroId() const
Returns the logical parent of all top-level RNTuple data fields.
const_iterator begin() const
const_iterator end() const
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2973
TDictAttributeMap * GetAttributeMap() const
TLine * line
const Int_t n
Definition legend1.C:16
ERNTupleSerializationMode
Possible settings for the "rntuple.streamerMode" class attribute in the dictionary.
std::vector< std::string > TokenizeTypeList(std::string_view templateType, std::size_t maxArgs=0)
Used in RFieldBase::Create() in order to get the comma-separated list of template types E....
ROOT::RLogChannel & NTupleLog()
Log channel for RNTuple diagnostics.
unsigned long long ParseUIntTypeToken(const std::string &uintToken)
std::string GetNormalizedInteger(const std::string &intTemplateArg)
Appends 'll' or 'ull' to the where necessary and strips the suffix if not needed.
ERNTupleSerializationMode GetRNTupleSerializationMode(TClass *cl)
std::string GetTypeTraceReport(const RFieldBase &field, const RNTupleDescriptor &desc)
Prints the hierarchy of types with their field names and field IDs for the given in-memory field and ...
std::string GetCanonicalTypePrefix(const std::string &typeName)
Applies RNTuple specific type name normalization rules (see specs) that help the string parsing in RF...
std::string GetNormalizedUnresolvedTypeName(const std::string &origName)
Applies all RNTuple type normalization rules except typedef resolution.
bool IsMatchingFieldType(const std::string &actualTypeName)
Helper to check if a given type name is the one expected of Field<T>.
Definition RField.hxx:551
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
long long ParseIntTypeToken(const std::string &intToken)
std::string GetDemangledTypeName(const std::type_info &t)
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
constexpr DescriptorId_t kInvalidDescriptorId
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 ...
char * DemangleName(const char *mangled_name, int &errorCode)
Definition TClassEdit.h:255
@ kDropComparator
Definition TClassEdit.h:84
@ kDropStlDefault
Definition TClassEdit.h:83