Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RFieldMeta.cxx
Go to the documentation of this file.
1/// \file RFieldMeta.cxx
2/// \ingroup NTuple
3/// \author Jonas Hahnfeld <jonas.hahnfeld@cern.ch>
4/// \date 2024-11-19
5
6// This file has concrete RField implementations that depend on ROOT Meta:
7// - RClassField
8// - REnumField
9// - RPairField
10// - RProxiedCollectionField
11// - RMapField
12// - RSetField
13// - RStreamerField
14// - RField<TObject>
15// - RVariantField
16
17#include <ROOT/RField.hxx>
18#include <ROOT/RFieldBase.hxx>
19#include <ROOT/RFieldUtils.hxx>
21#include <ROOT/RNTupleUtils.hxx>
22#include <ROOT/RSpan.hxx>
23
24#include <TBaseClass.h>
25#include <TBufferFile.h>
26#include <TClass.h>
27#include <TClassEdit.h>
28#include <TDataMember.h>
29#include <TEnum.h>
30#include <TObject.h>
31#include <TObjArray.h>
32#include <TObjString.h>
33#include <TRealData.h>
34#include <TSchemaRule.h>
35#include <TSchemaRuleSet.h>
36#include <TStreamerElement.h>
37#include <TVirtualObject.h>
39
40#include <algorithm>
41#include <array>
42#include <cstddef> // std::size_t
43#include <cstdint> // std::uint32_t et al.
44#include <cstring> // for memset
45#include <memory>
46#include <string>
47#include <string_view>
48#include <unordered_set>
49#include <utility>
50#include <variant>
51
53
54namespace {
55
56TClass *EnsureValidClass(std::string_view className)
57{
58 auto cl = TClass::GetClass(std::string(className).c_str());
59 if (cl == nullptr) {
60 throw ROOT::RException(R__FAIL("RField: no I/O support for type " + std::string(className)));
61 }
62 return cl;
63}
64
65TEnum *EnsureValidEnum(std::string_view enumName)
66{
67 auto e = TEnum::GetEnum(std::string(enumName).c_str());
68 if (e == nullptr) {
69 throw ROOT::RException(R__FAIL("RField: no I/O support for enum type " + std::string(enumName)));
70 }
71 return e;
72}
73
74/// Create a comma-separated list of type names from the given fields. Uses either the real type names or the
75/// type aliases (if there are any, otherwise the actual type name). Used to construct template argument lists
76/// for templated types such as std::pair<...>, std::tuple<...>, std::variant<...>.
77std::string GetTypeList(std::span<std::unique_ptr<ROOT::RFieldBase>> itemFields, bool useTypeAliases)
78{
79 std::string result;
80 for (size_t i = 0; i < itemFields.size(); ++i) {
81 if (useTypeAliases && !itemFields[i]->GetTypeAlias().empty()) {
82 result += itemFields[i]->GetTypeAlias();
83 } else {
84 result += itemFields[i]->GetTypeName();
85 }
86 result.push_back(',');
87 }
88 if (result.empty()) {
89 throw ROOT::RException(R__FAIL("invalid empty type list provided as template argument"));
90 }
91 result.pop_back(); // remove trailing comma
92 return result;
93}
94
96{
97 std::string typePrefix;
98 switch (setType) {
99 case ROOT::RSetField::ESetType::kSet: typePrefix = "std::set<"; break;
100 case ROOT::RSetField::ESetType::kUnorderedSet: typePrefix = "std::unordered_set<"; break;
101 case ROOT::RSetField::ESetType::kMultiSet: typePrefix = "std::multiset<"; break;
102 case ROOT::RSetField::ESetType::kUnorderedMultiSet: typePrefix = "std::unordered_multiset<"; break;
103 default: R__ASSERT(false);
104 }
105 return typePrefix +
106 ((useTypeAlias && !innerField.GetTypeAlias().empty()) ? innerField.GetTypeAlias()
107 : innerField.GetTypeName()) +
108 ">";
109}
110
112{
113 if (const auto pairField = dynamic_cast<const ROOT::RPairField *>(innerField)) {
114 std::string typePrefix;
115 switch (mapType) {
116 case ROOT::RMapField::EMapType::kMap: typePrefix = "std::map<"; break;
117 case ROOT::RMapField::EMapType::kUnorderedMap: typePrefix = "std::unordered_map<"; break;
118 case ROOT::RMapField::EMapType::kMultiMap: typePrefix = "std::multimap<"; break;
119 case ROOT::RMapField::EMapType::kUnorderedMultiMap: typePrefix = "std::unordered_multimap<"; break;
120 default: R__ASSERT(false);
121 }
122 const auto &items = pairField->GetConstSubfields();
123 std::string type = typePrefix;
124 for (int i : {0, 1}) {
125 if (useTypeAliases && !items[i]->GetTypeAlias().empty()) {
126 type += items[i]->GetTypeAlias();
127 } else {
128 type += items[i]->GetTypeName();
129 }
130 if (i == 0)
131 type.push_back(',');
132 }
133 return type + ">";
134 }
135
136 throw ROOT::RException(R__FAIL("RMapField inner field type must be of RPairField"));
137}
138
139} // anonymous namespace
140
142 : ROOT::RFieldBase(fieldName, source.GetTypeName(), ROOT::ENTupleStructure::kRecord, false /* isSimple */),
144 fSubfieldsInfo(source.fSubfieldsInfo),
145 fMaxAlignment(source.fMaxAlignment)
146{
147 for (const auto &f : source.GetConstSubfields()) {
148 RFieldBase::Attach(f->Clone(f->GetFieldName()));
149 }
150 fTraits = source.GetTraits();
151}
152
153ROOT::RClassField::RClassField(std::string_view fieldName, std::string_view className)
155{
156}
157
159 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kRecord,
160 false /* isSimple */),
162{
164 throw RException(R__FAIL(std::string("RField: RClassField \"") + classp->GetName() +
165 " cannot be constructed from a class that's not at least Interpreted"));
166 }
167 // Avoid accidentally supporting std types through TClass.
169 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
170 }
171 if (GetTypeName() == "TObject") {
172 throw RException(R__FAIL("TObject is only supported through RField<TObject>"));
173 }
174 if (fClass->GetCollectionProxy()) {
175 throw RException(R__FAIL(std::string(GetTypeName()) + " has an associated collection proxy; "
176 "use RProxiedCollectionField instead"));
177 }
178 // Classes with, e.g., custom streamers are not supported through this field. Empty classes, however, are.
179 // Can be overwritten with the "rntuple.streamerMode=true" class attribute
180 if (!fClass->CanSplit() && fClass->Size() > 1 &&
183 throw RException(R__FAIL(GetTypeName() + " cannot be stored natively in RNTuple"));
184 }
187 throw RException(R__FAIL(GetTypeName() + " has streamer mode enforced, not supported as native RNTuple class"));
188 }
189
194
195 std::string renormalizedAlias;
198
199 int i = 0;
200 const auto *bases = fClass->GetListOfBases();
201 assert(bases);
203 if (baseClass->GetDelta() < 0) {
204 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
205 " virtually inherits from " + baseClass->GetName()));
206 }
207 TClass *c = baseClass->GetClassPointer();
208 auto subField =
209 RFieldBase::Create(std::string(kPrefixInherited) + "_" + std::to_string(i), c->GetName()).Unwrap();
210 fTraits &= subField->GetTraits();
211 Attach(std::move(subField), RSubFieldInfo{kBaseClass, static_cast<std::size_t>(baseClass->GetDelta())});
212 i++;
213 }
215 // Skip, for instance, unscoped enum constants defined in the class
216 if (dataMember->Property() & kIsStatic)
217 continue;
218 // Skip members explicitly marked as transient by user comment
219 if (!dataMember->IsPersistent()) {
220 // TODO(jblomer): we could do better
222 continue;
223 }
224
225 // NOTE: we use the already-resolved type name for the fields, otherwise TClass::GetClass may fail to resolve
226 // context-dependent types (e.g. typedefs defined in the class itself - which will not be fully qualified in
227 // the string returned by dataMember->GetFullTypeName())
228 std::string typeName{dataMember->GetTrueTypeName()};
229 // RFieldBase::Create() set subField->fTypeAlias based on the assumption that the user specified typeName, which
230 // already went through one round of type resolution.
231 std::string origTypeName{dataMember->GetFullTypeName()};
232
233 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
234 if (dataMember->Property() & kIsArray) {
235 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim) {
236 const auto addedStr = "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
237 typeName += addedStr;
239 }
240 }
241
242 auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
243
245 if (normTypeName == subField->GetTypeName()) {
247 } else {
249 }
250
251 fTraits &= subField->GetTraits();
252 Attach(std::move(subField), RSubFieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
253 }
255}
256
258{
259 if (fStagingArea) {
260 for (const auto &[_, si] : fStagingItems) {
261 if (!(si.fField->GetTraits() & kTraitTriviallyDestructible)) {
262 auto deleter = GetDeleterOf(*si.fField);
263 deleter->operator()(fStagingArea.get() + si.fOffset, true /* dtorOnly */);
264 }
265 }
266 }
267}
268
269void ROOT::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubFieldInfo info)
270{
271 fMaxAlignment = std::max(fMaxAlignment, child->GetAlignment());
272 fSubfieldsInfo.push_back(info);
273 RFieldBase::Attach(std::move(child));
274}
275
276std::vector<const ROOT::TSchemaRule *> ROOT::RClassField::FindRules(const ROOT::RFieldDescriptor *fieldDesc)
277{
279 const auto ruleset = fClass->GetSchemaRules();
280 if (!ruleset)
281 return rules;
282
283 if (!fieldDesc) {
284 // If we have no on-disk information for the field, we still process the rules on the current in-memory version
285 // of the class
286 rules = ruleset->FindRules(fClass->GetName(), fClass->GetClassVersion(), fClass->GetCheckSum());
287 } else {
288 // We need to change (back) the name normalization from RNTuple to ROOT Meta
289 std::string normalizedName;
291 // We do have an on-disk field that correspond to the current RClassField instance. Ask for rules matching the
292 // on-disk version of the field.
293 if (fieldDesc->GetTypeChecksum()) {
294 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion(), *fieldDesc->GetTypeChecksum());
295 } else {
296 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion());
297 }
298 }
299
300 // Cleanup and sort rules
301 // Check that any any given source member uses the same type in all rules
302 std::unordered_map<std::string, std::string> sourceNameAndType;
303 std::size_t nskip = 0; // skip whole-object-rules that were moved to the end of the rules vector
304 for (auto itr = rules.begin(); itr != rules.end() - nskip;) {
305 const auto rule = *itr;
306
307 // Erase unknown rule types
308 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
310 << "ignoring I/O customization rule with unsupported type: " << rule->GetRuleType();
311 itr = rules.erase(itr);
312 continue;
313 }
314
315 bool hasConflictingSourceMembers = false;
316 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
317 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
318 auto [itrSrc, isNew] = sourceNameAndType.emplace(source->GetName(), memberType);
319 if (!isNew && (itrSrc->second != memberType)) {
321 << "ignoring I/O customization rule due to conflicting source member type: " << itrSrc->second << " vs. "
322 << memberType << " for member " << source->GetName();
324 break;
325 }
326 }
328 itr = rules.erase(itr);
329 continue;
330 }
331
332 // Rules targeting the entire object need to be executed at the end
333 if (rule->GetTarget() == nullptr) {
334 nskip++;
335 if (itr != rules.end() - nskip)
336 std::iter_swap(itr++, rules.end() - nskip);
337 continue;
338 }
339
340 ++itr;
341 }
342
343 return rules;
344}
345
346std::unique_ptr<ROOT::RFieldBase> ROOT::RClassField::CloneImpl(std::string_view newName) const
347{
348 return std::unique_ptr<RClassField>(new RClassField(newName, *this));
349}
350
351std::size_t ROOT::RClassField::AppendImpl(const void *from)
352{
353 std::size_t nbytes = 0;
354 for (unsigned i = 0; i < fSubfields.size(); i++) {
355 nbytes += CallAppendOn(*fSubfields[i], static_cast<const unsigned char *>(from) + fSubfieldsInfo[i].fOffset);
356 }
357 return nbytes;
358}
359
361{
362 for (const auto &[_, si] : fStagingItems) {
363 CallReadOn(*si.fField, globalIndex, fStagingArea.get() + si.fOffset);
364 }
365 for (unsigned i = 0; i < fSubfields.size(); i++) {
366 CallReadOn(*fSubfields[i], globalIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
367 }
368}
369
371{
372 for (const auto &[_, si] : fStagingItems) {
373 CallReadOn(*si.fField, localIndex, fStagingArea.get() + si.fOffset);
374 }
375 for (unsigned i = 0; i < fSubfields.size(); i++) {
376 CallReadOn(*fSubfields[i], localIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
377 }
378}
379
382{
385 return idSourceMember;
386
387 for (const auto &subFieldDesc : desc.GetFieldIterable(classFieldId)) {
388 const auto subFieldName = subFieldDesc.GetFieldName();
389 if (subFieldName.length() > 2 && subFieldName[0] == ':' && subFieldName[1] == '_') {
390 idSourceMember = LookupMember(desc, memberName, subFieldDesc.GetId());
392 return idSourceMember;
393 }
394 }
395
397}
398
399void ROOT::RClassField::SetStagingClass(const std::string &className, unsigned int classVersion)
400{
401 TClass::GetClass(className.c_str())->GetStreamerInfo(classVersion);
402 if (classVersion != GetTypeVersion() || className != GetTypeName()) {
403 fStagingClass = TClass::GetClass((className + std::string("@@") + std::to_string(classVersion)).c_str());
404 if (!fStagingClass) {
405 // For a rename rule, we may simply ask for the old class name
406 fStagingClass = TClass::GetClass(className.c_str());
407 }
408 } else {
409 fStagingClass = fClass;
410 }
411 R__ASSERT(fStagingClass);
412 R__ASSERT(static_cast<unsigned int>(fStagingClass->GetClassVersion()) == classVersion);
413}
414
415void ROOT::RClassField::PrepareStagingArea(const std::vector<const TSchemaRule *> &rules,
416 const ROOT::RNTupleDescriptor &desc,
418{
419 std::size_t stagingAreaSize = 0;
420 for (const auto rule : rules) {
421 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
422 auto [itr, isNew] = fStagingItems.emplace(source->GetName(), RStagingItem());
423 if (!isNew) {
424 // This source member has already been processed by another rule (and we only support one type per member)
425 continue;
426 }
427 RStagingItem &stagingItem = itr->second;
428
429 const auto memberFieldId = LookupMember(desc, source->GetName(), classFieldDesc.GetId());
431 throw RException(R__FAIL(std::string("cannot find on disk rule source member ") + GetTypeName() + "." +
432 source->GetName()));
433 }
434
435 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
436 auto memberField = Create("" /* we don't need a field name */, std::string(memberType)).Unwrap();
437 memberField->SetOnDiskId(memberFieldId);
438 auto fieldZero = std::make_unique<RFieldZero>();
440 fieldZero->Attach(std::move(memberField));
441 stagingItem.fField = std::move(fieldZero);
442
443 stagingItem.fOffset = fStagingClass->GetDataMemberOffset(source->GetName());
444 // Since we successfully looked up the source member in the RNTuple on-disk metadata, we expect it
445 // to be present in the TClass instance, too.
447 stagingAreaSize = std::max(stagingAreaSize, stagingItem.fOffset + stagingItem.fField->begin()->GetValueSize());
448 }
449 }
450
451 if (stagingAreaSize) {
452 R__ASSERT(static_cast<Int_t>(stagingAreaSize) <= fStagingClass->Size()); // we may have removed rules
453 // We use std::make_unique instead of MakeUninitArray to zero-initialize the staging area.
454 fStagingArea = std::make_unique<unsigned char[]>(stagingAreaSize);
455
456 for (const auto &[_, si] : fStagingItems) {
457 const auto &memberField = *si.fField->cbegin();
458 if (!(memberField.GetTraits() & kTraitTriviallyConstructible)) {
459 CallConstructValueOn(memberField, fStagingArea.get() + si.fOffset);
460 }
461 }
462 }
463}
464
466{
467 auto func = rule->GetReadFunctionPointer();
468 if (func == nullptr) {
469 // Can happen for rename rules
470 return;
471 }
472 fReadCallbacks.emplace_back([func, stagingClass = fStagingClass, stagingArea = fStagingArea.get()](void *target) {
473 TVirtualObject onfileObj{nullptr};
474 onfileObj.fClass = stagingClass;
475 onfileObj.fObject = stagingArea;
476 func(static_cast<char *>(target), &onfileObj);
477 onfileObj.fObject = nullptr; // TVirtualObject does not own the value
478 });
479}
480
482{
483 std::vector<const TSchemaRule *> rules;
484 // On-disk members that are not targeted by an I/O rule; all other sub fields of the in-memory class
485 // will be marked as artificial (added member in a new class version or member set by rule).
486 std::unordered_set<std::string> regularSubfields;
487 // We generally don't support changing the number of base classes, with the exception of changing from/to zero
488 // base classes. The variable stores the number of on-disk base classes.
489 int nOnDiskBaseClasses = 0;
490
491 if (GetOnDiskId() == kInvalidDescriptorId) {
492 // This can happen for added base classes or added members of class type
493 rules = FindRules(nullptr);
494 if (!rules.empty())
495 SetStagingClass(GetTypeName(), GetTypeVersion());
496 } else {
497 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
498 const ROOT::RNTupleDescriptor &desc = descriptorGuard.GetRef();
499 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
500
501 if (fieldDesc.GetStructure() == ENTupleStructure::kStreamer) {
502 // Streamer field on disk but meanwhile the type can be represented as a class field; replace this field
503 // by a streamer field to read the data from disk.
504 auto substitute = std::make_unique<RStreamerField>(GetFieldName(), GetTypeName());
505 substitute->SetOnDiskId(GetOnDiskId());
506 return substitute;
507 }
508
509 for (auto linkId : fieldDesc.GetLinkIds()) {
510 const auto &subFieldDesc = desc.GetFieldDescriptor(linkId);
511 regularSubfields.insert(subFieldDesc.GetFieldName());
512 if (!subFieldDesc.GetFieldName().empty() && subFieldDesc.GetFieldName()[0] == ':')
514 }
515
516 rules = FindRules(&fieldDesc);
517
518 // If we found a rule, we know it is valid to read on-disk data because we found the rule according to the on-disk
519 // (source) type name and version/checksum.
520 if (rules.empty()) {
521 // Otherwise we require compatible type names, after renormalization. GetTypeName() is already renormalized,
522 // but RNTuple data written with ROOT v6.34 might not have renormalized the field type name. Ask the
523 // RNTupleDescriptor, which knows about the spec version, for a fixed up type name.
525 if (GetTypeName() != descTypeName) {
526 throw RException(R__FAIL("incompatible type name for field " + GetFieldName() + ": " + GetTypeName() +
527 " vs. " + descTypeName));
528 }
529 }
530
531 if (!rules.empty()) {
532 SetStagingClass(fieldDesc.GetTypeName(), fieldDesc.GetTypeVersion());
533 PrepareStagingArea(rules, desc, fieldDesc);
534 for (auto &[_, si] : fStagingItems) {
536 si.fField = std::move(static_cast<RFieldZero *>(si.fField.get())->ReleaseSubfields()[0]);
537 }
538
539 // Remove target member of read rules from the list of regular members of the underlying on-disk field
540 for (const auto rule : rules) {
541 if (!rule->GetTarget())
542 continue;
543
544 for (const auto target : ROOT::Detail::TRangeStaticCast<const TObjString>(*rule->GetTarget())) {
545 regularSubfields.erase(std::string(target->GetString()));
546 }
547 }
548 }
549 }
550
551 for (const auto rule : rules) {
552 AddReadCallbacksFromIORule(rule);
553 }
554
555 // Iterate over all sub fields in memory and mark those as missing that are not in the descriptor.
556 int nInMemoryBaseClasses = 0;
557 for (auto &field : fSubfields) {
558 const auto &fieldName = field->GetFieldName();
559 if (regularSubfields.count(fieldName) == 0) {
560 CallSetArtificialOn(*field);
561 }
562 if (!fieldName.empty() && fieldName[0] == ':')
564 }
565
567 throw RException(R__FAIL(std::string("incompatible number of base classes for field ") + GetFieldName() + ": " +
568 GetTypeName() + ", " + std::to_string(nInMemoryBaseClasses) +
569 " base classes in memory "
570 " vs. " +
571 std::to_string(nOnDiskBaseClasses) + " base classes on-disk\n" +
572 Internal::GetTypeTraceReport(*this, pageSource.GetSharedDescriptorGuard().GetRef())));
573 }
574
575 return nullptr;
576}
577
579{
580 EnsureMatchingOnDiskField(desc, kDiffTypeVersion | kDiffTypeName).ThrowOnError();
581}
582
584{
585 fClass->New(where);
586}
587
589{
590 fClass->Destructor(objPtr, true /* dtorOnly */);
591 RDeleter::operator()(objPtr, dtorOnly);
592}
593
594std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue &value) const
595{
596 std::vector<RValue> result;
597 auto valuePtr = value.GetPtr<void>();
598 auto charPtr = static_cast<unsigned char *>(valuePtr.get());
599 result.reserve(fSubfields.size());
600 for (unsigned i = 0; i < fSubfields.size(); i++) {
601 result.emplace_back(
602 fSubfields[i]->BindValue(std::shared_ptr<void>(valuePtr, charPtr + fSubfieldsInfo[i].fOffset)));
603 }
604 return result;
605}
606
608{
609 return fClass->GetClassSize();
610}
611
613{
614 return fClass->GetClassVersion();
615}
616
618{
619 return fClass->GetCheckSum();
620}
621
622const std::type_info *ROOT::RClassField::GetPolymorphicTypeInfo() const
623{
624 bool polymorphic = fClass->ClassProperty() & kClassHasVirtual;
625 if (!polymorphic) {
626 return nullptr;
627 }
628 return fClass->GetTypeInfo();
629}
630
632{
633 visitor.VisitClassField(*this);
634}
635
636//------------------------------------------------------------------------------
637
638ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
640{
641}
642
644 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
645 false /* isSimple */)
646{
647 // Avoid accidentally supporting std types through TEnum.
648 if (enump->Property() & kIsDefinedInStd) {
649 throw RException(R__FAIL(GetTypeName() + " is not supported"));
650 }
651
652 switch (enump->GetUnderlyingType()) {
653 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
654 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
655 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
656 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
657 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
658 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
659 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
660 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
661 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
662 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
663 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
664 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
665 }
666
668}
669
670ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
671 std::unique_ptr<RFieldBase> intField)
673{
674 Attach(std::move(intField));
676}
677
678std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
679{
680 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
681 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
682}
683
685{
686 // TODO(jblomer): allow enum to enum conversion only by rename rule
687 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
688}
689
690std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
691{
692 std::vector<RValue> result;
693 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
694 return result;
695}
696
698{
699 visitor.VisitEnumField(*this);
700}
701
702//------------------------------------------------------------------------------
703
704ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields,
705 const std::array<std::size_t, 2> &offsets)
706 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
707{
708 const std::string typeAlias = "std::pair<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
709 if (typeAlias != GetTypeName())
711
712 AttachItemFields(std::move(itemFields));
713 fOffsets.push_back(offsets[0]);
714 fOffsets.push_back(offsets[1]);
715}
716
717ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
718 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
719{
720 const std::string typeAlias = "std::pair<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
721 if (typeAlias != GetTypeName())
723
724 AttachItemFields(std::move(itemFields));
725
726 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
727 auto *c = TClass::GetClass(GetTypeName().c_str());
728 if (!c)
729 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
730 fSize = c->Size();
731
732 auto firstElem = c->GetRealData("first");
733 if (!firstElem)
734 throw RException(R__FAIL("first: no such member"));
735 fOffsets.push_back(firstElem->GetThisOffset());
736
737 auto secondElem = c->GetRealData("second");
738 if (!secondElem)
739 throw RException(R__FAIL("second: no such member"));
740 fOffsets.push_back(secondElem->GetThisOffset());
741}
742
743std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
744{
745 std::array<std::size_t, 2> offsets = {fOffsets[0], fOffsets[1]};
746 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
747 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
748 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones), offsets));
749}
750
752{
753 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
754
755 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
756 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
757
758 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
759 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
760 if (nOnDiskSubfields != 2) {
761 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::pair " +
762 std::to_string(nOnDiskSubfields) + "\n" +
763 Internal::GetTypeTraceReport(*this, desc)));
764 }
765}
766
767//------------------------------------------------------------------------------
768
771 bool readFromDisk)
772{
774 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
775 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
776 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
777 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
778 (ifuncs.fNext != nullptr));
779 return ifuncs;
780}
781
783 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
784 false /* isSimple */),
785 fNWritten(0)
786{
787 if (!classp->GetCollectionProxy())
788 throw RException(R__FAIL(std::string(classp->GetName()) + " has no associated collection proxy"));
789 if (classp->Property() & kIsDefinedInStd) {
790 static const std::vector<std::string> supportedStdTypes = {
791 "std::set<", "std::unordered_set<", "std::multiset<", "std::unordered_multiset<",
792 "std::map<", "std::unordered_map<", "std::multimap<", "std::unordered_multimap<"};
793 bool isSupported = false;
794 for (const auto &tn : supportedStdTypes) {
795 if (GetTypeName().rfind(tn, 0) == 0) {
796 isSupported = true;
797 break;
798 }
799 }
800 if (!isSupported)
801 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
802 }
803
804 std::string renormalizedAlias;
807
808 fProxy.reset(classp->GetCollectionProxy()->Generate());
809 fProperties = fProxy->GetProperties();
810 fCollectionType = fProxy->GetCollectionType();
811 if (fProxy->HasPointers())
812 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
813
814 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
815 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
816}
817
818ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
820{
821 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
823 throw RException(R__FAIL("custom associative collection proxies not supported"));
824
825 std::unique_ptr<ROOT::RFieldBase> itemField;
826
827 if (auto valueClass = fProxy->GetValueClass()) {
828 // Element type is a class
829 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
830 } else {
831 switch (fProxy->GetType()) {
832 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
833 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
834 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
835 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
836 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
837 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
838 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
839 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
840 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
841 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
842 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
843 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
844 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
845 default: throw RException(R__FAIL("unsupported value type: " + std::to_string(fProxy->GetType())));
846 }
847 }
848
849 fItemSize = itemField->GetValueSize();
850 Attach(std::move(itemField));
851}
852
853std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
854{
855 auto clone =
856 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
857 clone->fItemSize = fItemSize;
858 clone->Attach(fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
859 return clone;
860}
861
862std::size_t ROOT::RProxiedCollectionField::AppendImpl(const void *from)
863{
864 std::size_t nbytes = 0;
865 unsigned count = 0;
866 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
867 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
868 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
869 nbytes += CallAppendOn(*fSubfields[0], ptr);
870 count++;
871 }
872
873 fNWritten += count;
874 fPrincipalColumn->Append(&fNWritten);
875 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
876}
877
879{
882 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
883
884 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
885 void *obj =
886 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
887
888 unsigned i = 0;
889 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
890 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
891 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
892 }
893 if (obj != to)
894 fProxy->Commit(obj);
895}
896
906
911
916
918{
919 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
920}
921
923{
924 fProxy->New(where);
925}
926
927std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
928{
929 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
930 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
931 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
932 }
933 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
934}
935
937{
938 if (fItemDeleter) {
940 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
941 fItemDeleter->operator()(ptr, true /* dtorOnly */);
942 }
943 }
944 fProxy->Destructor(objPtr, true /* dtorOnly */);
945 RDeleter::operator()(objPtr, dtorOnly);
946}
947
948std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
949{
950 std::vector<RValue> result;
951 auto valueRawPtr = value.GetPtr<void>().get();
953 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
954 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
955 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
956 }
957 return result;
958}
959
961{
962 visitor.VisitProxiedCollectionField(*this);
963}
964
965//------------------------------------------------------------------------------
966
967ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
969 EnsureValidClass(BuildMapTypeName(mapType, itemField.get(), false /* useTypeAliases */))),
970 fMapType(mapType)
971{
972 if (!itemField->GetTypeAlias().empty())
973 fTypeAlias = BuildMapTypeName(mapType, itemField.get(), true /* useTypeAliases */);
974
975 auto *itemClass = fProxy->GetValueClass();
976 fItemSize = itemClass->GetClassSize();
977
978 Attach(std::move(itemField), "_0");
979}
980
981std::unique_ptr<ROOT::RFieldBase> ROOT::RMapField::CloneImpl(std::string_view newName) const
982{
983 return std::make_unique<RMapField>(newName, fMapType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
984}
985
987{
988 static const std::vector<std::string> prefixesRegular = {"std::map<", "std::unordered_map<"};
989
990 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
991
992 switch (fMapType) {
993 case EMapType::kMap:
994 case EMapType::kUnorderedMap: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
995 default:
996 break;
997 // no restrictions for multimaps
998 }
999}
1000
1001//------------------------------------------------------------------------------
1002
1003ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
1005 EnsureValidClass(BuildSetTypeName(setType, *itemField, false /* useTypeAlias */))),
1006 fSetType(setType)
1007{
1008 if (!itemField->GetTypeAlias().empty())
1009 fTypeAlias = BuildSetTypeName(setType, *itemField, true /* useTypeAlias */);
1010
1011 fItemSize = itemField->GetValueSize();
1012
1013 Attach(std::move(itemField), "_0");
1014}
1015
1016std::unique_ptr<ROOT::RFieldBase> ROOT::RSetField::CloneImpl(std::string_view newName) const
1017{
1018 return std::make_unique<RSetField>(newName, fSetType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1019}
1020
1022{
1023 static const std::vector<std::string> prefixesRegular = {"std::set<", "std::unordered_set<", "std::map<",
1024 "std::unordered_map<"};
1025
1026 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1027
1028 switch (fSetType) {
1029 case ESetType::kSet:
1030 case ESetType::kUnorderedSet: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1031 default:
1032 break;
1033 // no restrictions for multisets
1034 }
1035}
1036
1037//------------------------------------------------------------------------------
1038
1039namespace {
1040
1041/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
1042class TBufferRecStreamer : public TBufferFile {
1043public:
1044 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
1045
1046private:
1047 RCallbackStreamerInfo fCallbackStreamerInfo;
1048
1049public:
1050 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
1051 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(callbackStreamerInfo)
1052 {
1053 }
1054 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
1055};
1056
1057} // anonymous namespace
1058
1059ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className)
1061{
1062}
1063
1065 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
1066 false /* isSimple */),
1067 fClass(classp),
1068 fIndex(0)
1069{
1070 std::string renormalizedAlias;
1073
1075 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
1076 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
1077 // implicit constructors and destructors.
1082}
1083
1084std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
1085{
1086 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, GetTypeName()));
1087}
1088
1089std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
1090{
1091 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
1092 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
1093 fClass->Streamer(const_cast<void *>(from), buffer);
1094
1095 auto nbytes = buffer.Length();
1096 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
1097 fIndex += nbytes;
1098 fPrincipalColumn->Append(&fIndex);
1099 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1100}
1101
1103{
1106 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
1107
1109 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
1110 fClass->Streamer(to, buffer);
1111}
1112
1122
1127
1132
1134{
1135 source.RegisterStreamerInfos();
1136 return nullptr;
1137}
1138
1140{
1141 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1142}
1143
1145{
1146 fClass->New(where);
1147}
1148
1150{
1151 fClass->Destructor(objPtr, true /* dtorOnly */);
1152 RDeleter::operator()(objPtr, dtorOnly);
1153}
1154
1156{
1159 .TypeVersion(GetTypeVersion())
1160 .TypeName(GetTypeName())
1162 return extraTypeInfoBuilder.MoveDescriptor().Unwrap();
1163}
1164
1166{
1167 return std::min(alignof(std::max_align_t), GetValueSize()); // TODO(jblomer): fix me
1168}
1169
1171{
1172 return fClass->GetClassSize();
1173}
1174
1176{
1177 return fClass->GetClassVersion();
1178}
1179
1181{
1182 return fClass->GetCheckSum();
1183}
1184
1186{
1187 visitor.VisitStreamerField(*this);
1188}
1189
1190//------------------------------------------------------------------------------
1191
1193{
1194 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1195 return dataMember->GetOffset();
1196 }
1197 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1198}
1199
1201 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1202{
1204 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1205 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1206}
1207
1209 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1210{
1211 assert(TObject::Class()->GetClassVersion() == 1);
1212
1214 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1215 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1216}
1217
1218std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1219{
1220 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1221}
1222
1223std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1224{
1225 // Cf. TObject::Streamer()
1226
1227 auto *obj = static_cast<const TObject *>(from);
1228 if (obj->TestBit(TObject::kIsReferenced)) {
1229 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1230 }
1231
1232 std::size_t nbytes = 0;
1233 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1234
1235 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1236 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1237 nbytes += CallAppendOn(*fSubfields[1], &bits);
1238
1239 return nbytes;
1240}
1241
1243{
1244 // Cf. TObject::Streamer()
1245
1246 auto *obj = static_cast<TObject *>(to);
1247 if (obj->TestBit(TObject::kIsReferenced)) {
1248 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1249 }
1250
1251 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1252
1253 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1255 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1256}
1257
1259{
1260 UInt_t uniqueID, bits;
1261 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1262 CallReadOn(*fSubfields[1], globalIndex, &bits);
1263 ReadTObject(to, uniqueID, bits);
1264}
1265
1267{
1268 UInt_t uniqueID, bits;
1269 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1270 CallReadOn(*fSubfields[1], localIndex, &bits);
1271 ReadTObject(to, uniqueID, bits);
1272}
1273
1275{
1276 return TObject::Class()->GetClassVersion();
1277}
1278
1280{
1281 return TObject::Class()->GetCheckSum();
1282}
1283
1285{
1286 new (where) TObject();
1287}
1288
1289std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1290{
1291 std::vector<RValue> result;
1292 // Use GetPtr<TObject> to type-check
1293 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1294 auto charPtr = static_cast<unsigned char *>(ptr.get());
1295 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1296 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1297 return result;
1298}
1299
1301{
1302 return sizeof(TObject);
1303}
1304
1306{
1307 return alignof(TObject);
1308}
1309
1311{
1312 visitor.VisitTObjectField(*this);
1313}
1314
1315//------------------------------------------------------------------------------
1316
1317ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields,
1318 const std::vector<std::size_t> &offsets)
1319 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1320{
1321 const std::string typeAlias = "std::tuple<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1322 if (typeAlias != GetTypeName())
1324
1325 AttachItemFields(std::move(itemFields));
1326 fOffsets = offsets;
1327}
1328
1329ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1330 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1331{
1332 const std::string typeAlias = "std::tuple<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1333 if (typeAlias != GetTypeName())
1335
1336 AttachItemFields(std::move(itemFields));
1337
1338 auto *c = TClass::GetClass(GetTypeName().c_str());
1339 if (!c)
1340 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1341 fSize = c->Size();
1342
1343 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1344 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1345 // following the order of the type list.
1346 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1347 // members, the assertion below will fail.
1348 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1349 std::string memberName("_" + std::to_string(i));
1350 auto member = c->GetRealData(memberName.c_str());
1351 if (!member)
1352 throw RException(R__FAIL(memberName + ": no such member"));
1353 fOffsets.push_back(member->GetThisOffset());
1354 }
1355}
1356
1357std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1358{
1359 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1360 itemClones.reserve(fSubfields.size());
1361 for (const auto &f : fSubfields) {
1362 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1363 }
1364 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones), fOffsets));
1365}
1366
1368{
1369 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1370
1371 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1372 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1373
1374 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1375 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1376 const auto nSubfields = fSubfields.size();
1378 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1379 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields) + "\n" +
1380 Internal::GetTypeTraceReport(*this, desc)));
1381 }
1382}
1383
1384//------------------------------------------------------------------------------
1385
1386namespace {
1387
1388// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1389constexpr std::size_t GetVariantTagSize()
1390{
1391 // Should be all zeros except for the tag, which is 1
1392 std::variant<char> t;
1393 constexpr auto sizeOfT = sizeof(t);
1394
1395 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1396 return sizeOfT == 2 ? 1 : 4;
1397}
1398
1399template <std::size_t VariantSizeT>
1400struct RVariantTag {
1401 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1402 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1403};
1404
1405} // anonymous namespace
1406
1408 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1409 fMaxItemSize(source.fMaxItemSize),
1410 fMaxAlignment(source.fMaxAlignment),
1411 fTagOffset(source.fTagOffset),
1412 fVariantOffset(source.fVariantOffset),
1413 fNWritten(source.fNWritten.size(), 0)
1414{
1415 for (const auto &f : source.GetConstSubfields())
1416 Attach(f->Clone(f->GetFieldName()));
1417 fTraits = source.fTraits;
1418}
1419
1420ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1421 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">",
1422 ROOT::ENTupleStructure::kVariant, false /* isSimple */)
1423{
1424 // The variant needs to initialize its own tag member
1426
1427 const std::string typeAlias = "std::variant<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1428 if (typeAlias != GetTypeName())
1430
1431 auto nFields = itemFields.size();
1432 if (nFields == 0 || nFields > kMaxVariants) {
1433 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
1434 }
1435 fNWritten.resize(nFields, 0);
1436 for (unsigned int i = 0; i < nFields; ++i) {
1439 fTraits &= itemFields[i]->GetTraits();
1440 Attach(std::move(itemFields[i]), "_" + std::to_string(i));
1441 }
1442
1443 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
1444 // For instance, std::variant<std::optional<int>> on macOS.
1445 auto cl = TClass::GetClass(GetTypeName().c_str());
1446 assert(cl);
1447 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
1448 if (dm)
1449 fVariantOffset = dm->GetOffset();
1450
1451 const auto tagSize = GetVariantTagSize();
1452 const auto padding = tagSize - (fMaxItemSize % tagSize);
1454}
1455
1456std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
1457{
1458 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
1459}
1460
1461std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
1462{
1463 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1464 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
1465 return (tag == TagType_t(-1)) ? 0 : tag + 1;
1466}
1467
1468void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
1469{
1470 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1471 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
1472 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
1473}
1474
1475std::size_t ROOT::RVariantField::AppendImpl(const void *from)
1476{
1477 auto tag = GetTag(from, fTagOffset);
1478 std::size_t nbytes = 0;
1479 auto index = 0;
1480 if (tag > 0) {
1481 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
1482 index = fNWritten[tag - 1]++;
1483 }
1485 fPrincipalColumn->Append(&varSwitch);
1486 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
1487}
1488
1490{
1492 std::uint32_t tag;
1493 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
1494 R__ASSERT(tag < 256);
1495
1496 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
1497 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
1498 // any `std::holds_alternative<T>` check fail later.
1499 if (R__likely(tag > 0)) {
1500 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
1501 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
1502 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
1503 }
1504 SetTag(to, fTagOffset, tag);
1505}
1506
1512
1517
1522
1524{
1525 static const std::vector<std::string> prefixes = {"std::variant<"};
1526
1527 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1528 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1529
1530 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1531 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
1532 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName() + "\n" +
1533 Internal::GetTypeTraceReport(*this, desc)));
1534 }
1535}
1536
1538{
1539 memset(where, 0, GetValueSize());
1540 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
1541 SetTag(where, fTagOffset, 1);
1542}
1543
1545{
1546 auto tag = GetTag(objPtr, fTagOffset);
1547 if (tag > 0) {
1548 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
1549 }
1550 RDeleter::operator()(objPtr, dtorOnly);
1551}
1552
1553std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
1554{
1555 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
1556 itemDeleters.reserve(fSubfields.size());
1557 for (const auto &f : fSubfields) {
1558 itemDeleters.emplace_back(GetDeleterOf(*f));
1559 }
1560 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, std::move(itemDeleters));
1561}
1562
1564{
1565 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
1566}
1567
1569{
1570 const auto alignment = GetAlignment();
1571 const auto actualSize = fTagOffset + GetVariantTagSize();
1572 const auto padding = alignment - (actualSize % alignment);
1573 return actualSize + ((padding == alignment) ? 0 : padding);
1574}
1575
1577{
1578 std::fill(fNWritten.begin(), fNWritten.end(), 0);
1579}
Cppyy::TCppType_t fClass
#define R__likely(expr)
Definition RConfig.hxx:593
#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 f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#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.
@ kFloat_t
Definition TDataType.h:31
@ kULong64_t
Definition TDataType.h:32
@ kInt_t
Definition TDataType.h:30
@ kLong_t
Definition TDataType.h:30
@ kShort_t
Definition TDataType.h:29
@ kBool_t
Definition TDataType.h:32
@ kULong_t
Definition TDataType.h:30
@ kLong64_t
Definition TDataType.h:32
@ kUShort_t
Definition TDataType.h:29
@ kDouble_t
Definition TDataType.h:31
@ kChar_t
Definition TDataType.h:29
@ kUChar_t
Definition TDataType.h:29
@ kUInt_t
Definition TDataType.h:30
@ kClassHasExplicitCtor
@ kClassHasImplicitCtor
@ kClassHasVirtual
@ kClassHasExplicitDtor
@ kClassHasImplicitDtor
@ kIsArray
Definition TDictionary.h:79
@ kIsStatic
Definition TDictionary.h:80
@ kIsDefinedInStd
Definition TDictionary.h:98
#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 Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t child
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 mode
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:110
TCanvas * alignment()
Definition alignment.C:1
#define _(A, B)
Definition cfortran.h:108
Abstract base class for classes implementing the visitor design pattern.
Holds the index and the tag of a kSwitch column.
A helper class for piece-wise construction of an RExtraTypeInfoDescriptor.
static std::string SerializeStreamerInfos(const StreamerInfoMap_t &infos)
Abstract interface to read data from an ntuple.
void operator()(void *objPtr, bool dtorOnly) final
The field for a class with dictionary.
Definition RField.hxx:138
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
void AddReadCallbacksFromIORule(const TSchemaRule *rule)
Register post-read callback corresponding to a ROOT I/O customization rules.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.hxx:226
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void Attach(std::unique_ptr< RFieldBase > child, RSubFieldInfo info)
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
std::vector< const TSchemaRule * > FindRules(const ROOT::RFieldDescriptor *fieldDesc)
Given the on-disk information from the page source, find all the I/O customization rules that apply t...
ROOT::DescriptorId_t LookupMember(const ROOT::RNTupleDescriptor &desc, std::string_view memberName, ROOT::DescriptorId_t classFieldId)
Returns the id of member 'name' in the class field given by 'fieldId', or kInvalidDescriptorId if no ...
void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final
TClass * fClass
Definition RField.hxx:167
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
RClassField(std::string_view fieldName, const RClassField &source)
Used by CloneImpl.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
void PrepareStagingArea(const std::vector< const TSchemaRule * > &rules, const ROOT::RNTupleDescriptor &desc, const ROOT::RFieldDescriptor &classFieldId)
If there are rules with inputs (source members), create the staging area according to the TClass inst...
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
const std::type_info * GetPolymorphicTypeInfo() const
For polymorphic classes (that declare or inherit at least one virtual method), return the expected dy...
~RClassField() override
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
static constexpr const char * kPrefixInherited
Prefix used in the subfield names generated for base classes.
Definition RField.hxx:156
void SetStagingClass(const std::string &className, unsigned int classVersion)
Sets fStagingClass according to the given name and version.
The field for an unscoped or scoped enum with dictionary.
Definition RField.hxx:293
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
REnumField(std::string_view fieldName, TEnum *enump)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
Field specific extra type information from the header / extenstion header.
The list of column representations a field can have.
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
A field translates read and write calls from/to underlying columns to/from tree values.
void Attach(std::unique_ptr< RFieldBase > child, std::string_view expectedChildName="")
Add a new subfield to the list of nested fields.
static void SetTypeAliasOf(RFieldBase &other, const std::string &alias)
Allow class fields to adjust the type alias of their members.
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
std::uint32_t fTraits
Properties of the type that allow for optimizations of collections of that type.
static RResult< std::unique_ptr< RFieldBase > > Create(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
Factory method to resurrect a field from the stored on-disk type information.
std::string fTypeAlias
A typedef or using name that was used when creating the field.
const std::string & GetTypeName() const
@ kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
@ kTraitTriviallyConstructible
No constructor needs to be called, i.e.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
Metadata stored for every field of an RNTuple.
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:59
std::vector< std::unique_ptr< RFieldBase > > ReleaseSubfields()
Moves all subfields into the returned vector.
Definition RField.cxx:65
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:323
RField(std::string_view name)
Definition RField.hxx:326
RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr< RFieldBase > itemField)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
The on-storage metadata of an RNTuple.
RFieldDescriptorIterable GetFieldIterable(const RFieldDescriptor &fieldDesc) const
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
Template specializations for C++ std::pair.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RPairField(std::string_view fieldName, std::array< std::unique_ptr< RFieldBase >, 2 > itemFields, const std::array< std::size_t, 2 > &offsets)
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Allows for iterating over the elements of a proxied collection.
static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk)
void operator()(void *objPtr, bool dtorOnly) final
The field for a class representing a collection of elements via TVirtualCollectionProxy.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
RProxiedCollectionField(std::string_view fieldName, TClass *classp)
Constructor used when the value type of the collection is not known in advance, i....
RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RCollectionIterableOnce::RIteratorFuncs fIFuncsRead
Two sets of functions to operate on iterators, to be used depending on the access type.
std::shared_ptr< TVirtualCollectionProxy > fProxy
The collection proxy is needed by the deleters and thus defined as a shared pointer.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) override
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
const_iterator begin() const
const_iterator end() const
The field for an untyped record.
void AttachItemFields(ContainerT &&itemFields)
std::vector< std::size_t > fOffsets
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr< RFieldBase > itemField)
void operator()(void *objPtr, bool dtorOnly) final
The field for a class using ROOT standard streaming.
Definition RField.hxx:238
ROOT::RExtraTypeInfoDescriptor GetExtraTypeInfo() const final
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &source) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RStreamerField(std::string_view fieldName, TClass *classp)
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
Template specializations for C++ std::tuple.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RTupleField(std::string_view fieldName, std::vector< std::unique_ptr< RFieldBase > > itemFields, const std::vector< std::size_t > &offsets)
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void operator()(void *objPtr, bool dtorOnly) final
Template specializations for C++ std::variant.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
static constexpr std::size_t kMaxVariants
std::vector< ROOT::Internal::RColumnIndex::ValueType > fNWritten
static std::uint8_t GetTag(const void *variantPtr, std::size_t tagOffset)
Extracts the index from an std::variant and transforms it into the 1-based index used for the switch ...
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
size_t fVariantOffset
In the std::variant memory layout, the actual union of types may start at an offset > 0.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
size_t fTagOffset
In the std::variant memory layout, at which byte number is the index stored.
RVariantField(std::string_view name, const RVariantField &source)
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
static void SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
void CommitClusterImpl() final
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
Definition TBufferFile.h:47
@ kWrite
Definition TBuffer.h:73
@ kRead
Definition TBuffer.h:73
char * Buffer() const
Definition TBuffer.h:96
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
Bool_t CanSplit() const
Return true if the data member of this TClass can be saved separately.
Definition TClass.cxx:2324
EState GetState() const
Definition TClass.h:501
TList * GetListOfDataMembers(Bool_t load=kTRUE)
Return list containing the TDataMembers of a class.
Definition TClass.cxx:3797
Int_t Size() const
Return size of object of this class.
Definition TClass.cxx:5743
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition TClass.cxx:3663
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2902
Long_t ClassProperty() const
Return the C++ property of this class, eg.
Definition TClass.cxx:2401
Long_t Property() const override
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition TClass.cxx:6128
@ kInterpreted
Definition TClass.h:129
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
All ROOT classes may have RTTI (run time type identification) support added.
Definition TDataMember.h:31
The TEnum class implements the enum type.
Definition TEnum.h:33
static TEnum * GetEnum(const std::type_info &ti, ESearchAction sa=kALoadAndInterpLookup)
Definition TEnum.cxx:181
Mother of all ROOT objects.
Definition TObject.h:42
@ kIsOnHeap
object is on heap
Definition TObject.h:89
@ kNotDeleted
object has not been deleted
Definition TObject.h:90
static TClass * Class()
@ kIsReferenced
if object is referenced by a TRef or TRefArray
Definition TObject.h:73
RAII helper class that ensures that PushProxy() / PopProxy() are called when entering / leaving a C++...
Defines a common interface to inspect/change the contents of an object that represents a collection.
@ kNeedDelete
The collection contains directly or indirectly (via other collection) some pointers that need explici...
Abstract Interface class describing Streamer information for one class.
const Int_t n
Definition legend1.C:16
void SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val)
Definition RField.cxx:36
ROOT::RLogChannel & NTupleLog()
Log channel for RNTuple diagnostics.
void CallConnectPageSourceOnField(RFieldBase &, ROOT::Internal::RPageSource &)
bool NeedsMetaNameAsAlias(const std::string &metaNormalizedName, std::string &renormalizedAlias, bool isArgInTemplatedUserClass=false)
Checks if the meta normalized name is different from the RNTuple normalized name in a way that would ...
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 GetNormalizedUnresolvedTypeName(const std::string &origName)
Applies all RNTuple type normalization rules except typedef resolution.
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
@ kSTLvector
Definition ESTLType.h:30
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
ENTupleStructure
The fields in the RNTuple data model tree can carry different structural information about the type s...
void GetNormalizedName(std::string &norm_name, std::string_view name)
Return the normalized name.