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
75{
76 std::string typePrefix;
77 switch (setType) {
78 case ROOT::RSetField::ESetType::kSet: typePrefix = "std::set<"; break;
79 case ROOT::RSetField::ESetType::kUnorderedSet: typePrefix = "std::unordered_set<"; break;
80 case ROOT::RSetField::ESetType::kMultiSet: typePrefix = "std::multiset<"; break;
81 case ROOT::RSetField::ESetType::kUnorderedMultiSet: typePrefix = "std::unordered_multiset<"; break;
82 default: R__ASSERT(false);
83 }
84 return typePrefix + innerField.GetTypeName() + ">";
85}
86
88{
89 if (const auto pairField = dynamic_cast<const ROOT::RPairField *>(innerField)) {
90 std::string typePrefix;
91 switch (mapType) {
92 case ROOT::RMapField::EMapType::kMap: typePrefix = "std::map<"; break;
93 case ROOT::RMapField::EMapType::kUnorderedMap: typePrefix = "std::unordered_map<"; break;
94 case ROOT::RMapField::EMapType::kMultiMap: typePrefix = "std::multimap<"; break;
95 case ROOT::RMapField::EMapType::kUnorderedMultiMap: typePrefix = "std::unordered_multimap<"; break;
96 default: R__ASSERT(false);
97 }
98 auto subFields = pairField->GetConstSubfields();
99 return typePrefix + subFields[0]->GetTypeName() + "," + subFields[1]->GetTypeName() + ">";
100 }
101
102 throw ROOT::RException(R__FAIL("RMapField inner field type must be of RPairField"));
103}
104
105} // anonymous namespace
106
108 : ROOT::RFieldBase(fieldName, source.GetTypeName(), ROOT::ENTupleStructure::kRecord, false /* isSimple */),
110 fSubfieldsInfo(source.fSubfieldsInfo),
111 fMaxAlignment(source.fMaxAlignment)
112{
113 for (const auto &f : source.GetConstSubfields()) {
114 RFieldBase::Attach(f->Clone(f->GetFieldName()));
115 }
116 fTraits = source.GetTraits();
117}
118
119ROOT::RClassField::RClassField(std::string_view fieldName, std::string_view className)
121{
122}
123
125 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kRecord,
126 false /* isSimple */),
128{
130 throw RException(R__FAIL(std::string("RField: RClassField \"") + classp->GetName() +
131 " cannot be constructed from a class that's not at least Interpreted"));
132 }
133 // Avoid accidentally supporting std types through TClass.
135 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
136 }
137 if (GetTypeName() == "TObject") {
138 throw RException(R__FAIL("TObject is only supported through RField<TObject>"));
139 }
140 if (fClass->GetCollectionProxy()) {
141 throw RException(R__FAIL(std::string(GetTypeName()) + " has an associated collection proxy; "
142 "use RProxiedCollectionField instead"));
143 }
144 // Classes with, e.g., custom streamers are not supported through this field. Empty classes, however, are.
145 // Can be overwritten with the "rntuple.streamerMode=true" class attribute
146 if (!fClass->CanSplit() && fClass->Size() > 1 &&
149 throw RException(R__FAIL(GetTypeName() + " cannot be stored natively in RNTuple"));
150 }
153 throw RException(R__FAIL(GetTypeName() + " has streamer mode enforced, not supported as native RNTuple class"));
154 }
155
160
161 std::string renormalizedAlias;
164
165 int i = 0;
166 const auto *bases = fClass->GetListOfBases();
167 assert(bases);
169 if (baseClass->GetDelta() < 0) {
170 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
171 " virtually inherits from " + baseClass->GetName()));
172 }
173 TClass *c = baseClass->GetClassPointer();
174 auto subField =
175 RFieldBase::Create(std::string(kPrefixInherited) + "_" + std::to_string(i), c->GetName()).Unwrap();
176 fTraits &= subField->GetTraits();
177 Attach(std::move(subField), RSubFieldInfo{kBaseClass, static_cast<std::size_t>(baseClass->GetDelta())});
178 i++;
179 }
181 // Skip, for instance, unscoped enum constants defined in the class
182 if (dataMember->Property() & kIsStatic)
183 continue;
184 // Skip members explicitly marked as transient by user comment
185 if (!dataMember->IsPersistent()) {
186 // TODO(jblomer): we could do better
188 continue;
189 }
190
191 // NOTE: we use the already-resolved type name for the fields, otherwise TClass::GetClass may fail to resolve
192 // context-dependent types (e.g. typedefs defined in the class itself - which will not be fully qualified in
193 // the string returned by dataMember->GetFullTypeName())
194 std::string typeName{dataMember->GetTrueTypeName()};
195 // RFieldBase::Create() set subField->fTypeAlias based on the assumption that the user specified typeName, which
196 // already went through one round of type resolution.
197 std::string origTypeName{dataMember->GetFullTypeName()};
198
199 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
200 if (dataMember->Property() & kIsArray) {
201 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim) {
202 const auto addedStr = "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
203 typeName += addedStr;
205 }
206 }
207
208 auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
209
211 if (normTypeName == subField->GetTypeName()) {
213 } else {
215 }
216
217 fTraits &= subField->GetTraits();
218 Attach(std::move(subField), RSubFieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
219 }
221}
222
224{
225 if (fStagingArea) {
226 for (const auto &[_, si] : fStagingItems) {
227 if (!(si.fField->GetTraits() & kTraitTriviallyDestructible)) {
228 auto deleter = GetDeleterOf(*si.fField);
229 deleter->operator()(fStagingArea.get() + si.fOffset, true /* dtorOnly */);
230 }
231 }
232 }
233}
234
235void ROOT::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubFieldInfo info)
236{
237 fMaxAlignment = std::max(fMaxAlignment, child->GetAlignment());
238 fSubfieldsInfo.push_back(info);
239 RFieldBase::Attach(std::move(child));
240}
241
242std::vector<const ROOT::TSchemaRule *> ROOT::RClassField::FindRules(const ROOT::RFieldDescriptor *fieldDesc)
243{
245 const auto ruleset = fClass->GetSchemaRules();
246 if (!ruleset)
247 return rules;
248
249 if (!fieldDesc) {
250 // If we have no on-disk information for the field, we still process the rules on the current in-memory version
251 // of the class
252 rules = ruleset->FindRules(fClass->GetName(), fClass->GetClassVersion(), fClass->GetCheckSum());
253 } else {
254 // We need to change (back) the name normalization from RNTuple to ROOT Meta
255 std::string normalizedName;
257 // We do have an on-disk field that correspond to the current RClassField instance. Ask for rules matching the
258 // on-disk version of the field.
259 if (fieldDesc->GetTypeChecksum()) {
260 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion(), *fieldDesc->GetTypeChecksum());
261 } else {
262 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion());
263 }
264 }
265
266 // Cleanup and sort rules
267 // Check that any any given source member uses the same type in all rules
268 std::unordered_map<std::string, std::string> sourceNameAndType;
269 std::size_t nskip = 0; // skip whole-object-rules that were moved to the end of the rules vector
270 for (auto itr = rules.begin(); itr != rules.end() - nskip;) {
271 const auto rule = *itr;
272
273 // Erase unknown rule types
274 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
276 << "ignoring I/O customization rule with unsupported type: " << rule->GetRuleType();
277 itr = rules.erase(itr);
278 continue;
279 }
280
281 bool hasConflictingSourceMembers = false;
282 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
283 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
284 auto [itrSrc, isNew] = sourceNameAndType.emplace(source->GetName(), memberType);
285 if (!isNew && (itrSrc->second != memberType)) {
287 << "ignoring I/O customization rule due to conflicting source member type: " << itrSrc->second << " vs. "
288 << memberType << " for member " << source->GetName();
290 break;
291 }
292 }
294 itr = rules.erase(itr);
295 continue;
296 }
297
298 // Rules targeting the entire object need to be executed at the end
299 if (rule->GetTarget() == nullptr) {
300 nskip++;
301 if (itr != rules.end() - nskip)
302 std::iter_swap(itr++, rules.end() - nskip);
303 continue;
304 }
305
306 ++itr;
307 }
308
309 return rules;
310}
311
312std::unique_ptr<ROOT::RFieldBase> ROOT::RClassField::CloneImpl(std::string_view newName) const
313{
314 return std::unique_ptr<RClassField>(new RClassField(newName, *this));
315}
316
317std::size_t ROOT::RClassField::AppendImpl(const void *from)
318{
319 std::size_t nbytes = 0;
320 for (unsigned i = 0; i < fSubfields.size(); i++) {
321 nbytes += CallAppendOn(*fSubfields[i], static_cast<const unsigned char *>(from) + fSubfieldsInfo[i].fOffset);
322 }
323 return nbytes;
324}
325
327{
328 for (const auto &[_, si] : fStagingItems) {
329 CallReadOn(*si.fField, globalIndex, fStagingArea.get() + si.fOffset);
330 }
331 for (unsigned i = 0; i < fSubfields.size(); i++) {
332 CallReadOn(*fSubfields[i], globalIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
333 }
334}
335
337{
338 for (const auto &[_, si] : fStagingItems) {
339 CallReadOn(*si.fField, localIndex, fStagingArea.get() + si.fOffset);
340 }
341 for (unsigned i = 0; i < fSubfields.size(); i++) {
342 CallReadOn(*fSubfields[i], localIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
343 }
344}
345
348{
351 return idSourceMember;
352
353 for (const auto &subFieldDesc : desc.GetFieldIterable(classFieldId)) {
354 const auto subFieldName = subFieldDesc.GetFieldName();
355 if (subFieldName.length() > 2 && subFieldName[0] == ':' && subFieldName[1] == '_') {
356 idSourceMember = LookupMember(desc, memberName, subFieldDesc.GetId());
358 return idSourceMember;
359 }
360 }
361
363}
364
365void ROOT::RClassField::SetStagingClass(const std::string &className, unsigned int classVersion)
366{
367 TClass::GetClass(className.c_str())->GetStreamerInfo(classVersion);
368 if (classVersion != GetTypeVersion() || className != GetTypeName()) {
369 fStagingClass = TClass::GetClass((className + std::string("@@") + std::to_string(classVersion)).c_str());
370 if (!fStagingClass) {
371 // For a rename rule, we may simply ask for the old class name
372 fStagingClass = TClass::GetClass(className.c_str());
373 }
374 } else {
375 fStagingClass = fClass;
376 }
377 R__ASSERT(fStagingClass);
378 R__ASSERT(static_cast<unsigned int>(fStagingClass->GetClassVersion()) == classVersion);
379}
380
381void ROOT::RClassField::PrepareStagingArea(const std::vector<const TSchemaRule *> &rules,
382 const ROOT::RNTupleDescriptor &desc,
384{
385 std::size_t stagingAreaSize = 0;
386 for (const auto rule : rules) {
387 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
388 auto [itr, isNew] = fStagingItems.emplace(source->GetName(), RStagingItem());
389 if (!isNew) {
390 // This source member has already been processed by another rule (and we only support one type per member)
391 continue;
392 }
393 RStagingItem &stagingItem = itr->second;
394
395 const auto memberFieldId = LookupMember(desc, source->GetName(), classFieldDesc.GetId());
397 throw RException(R__FAIL(std::string("cannot find on disk rule source member ") + GetTypeName() + "." +
398 source->GetName()));
399 }
400
401 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
402 auto memberField = Create("" /* we don't need a field name */, std::string(memberType)).Unwrap();
403 memberField->SetOnDiskId(memberFieldId);
404 auto fieldZero = std::make_unique<RFieldZero>();
406 fieldZero->Attach(std::move(memberField));
407 stagingItem.fField = std::move(fieldZero);
408
409 stagingItem.fOffset = fStagingClass->GetDataMemberOffset(source->GetName());
410 // Since we successfully looked up the source member in the RNTuple on-disk metadata, we expect it
411 // to be present in the TClass instance, too.
413 stagingAreaSize = std::max(stagingAreaSize, stagingItem.fOffset + stagingItem.fField->begin()->GetValueSize());
414 }
415 }
416
417 if (stagingAreaSize) {
418 R__ASSERT(static_cast<Int_t>(stagingAreaSize) <= fStagingClass->Size()); // we may have removed rules
419 // We use std::make_unique instead of MakeUninitArray to zero-initialize the staging area.
420 fStagingArea = std::make_unique<unsigned char[]>(stagingAreaSize);
421
422 for (const auto &[_, si] : fStagingItems) {
423 const auto &memberField = *si.fField->cbegin();
424 if (!(memberField.GetTraits() & kTraitTriviallyConstructible)) {
425 CallConstructValueOn(memberField, fStagingArea.get() + si.fOffset);
426 }
427 }
428 }
429}
430
432{
433 auto func = rule->GetReadFunctionPointer();
434 if (func == nullptr) {
435 // Can happen for rename rules
436 return;
437 }
438 fReadCallbacks.emplace_back([func, stagingClass = fStagingClass, stagingArea = fStagingArea.get()](void *target) {
439 TVirtualObject onfileObj{nullptr};
440 onfileObj.fClass = stagingClass;
441 onfileObj.fObject = stagingArea;
442 func(static_cast<char *>(target), &onfileObj);
443 onfileObj.fObject = nullptr; // TVirtualObject does not own the value
444 });
445}
446
448{
449 std::vector<const TSchemaRule *> rules;
450 // On-disk members that are not targeted by an I/O rule; all other sub fields of the in-memory class
451 // will be marked as artificial (added member in a new class version or member set by rule).
452 std::unordered_set<std::string> regularSubfields;
453 // We generally don't support changing the number of base classes, with the exception of changing from/to zero
454 // base classes. The variable stores the number of on-disk base classes.
455 int nOnDiskBaseClasses = 0;
456
457 if (GetOnDiskId() == kInvalidDescriptorId) {
458 // This can happen for added base classes or added members of class type
459 rules = FindRules(nullptr);
460 if (!rules.empty())
461 SetStagingClass(GetTypeName(), GetTypeVersion());
462 } else {
463 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
464 const ROOT::RNTupleDescriptor &desc = descriptorGuard.GetRef();
465 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
466
467 if (fieldDesc.GetStructure() == ENTupleStructure::kStreamer) {
468 // Streamer field on disk but meanwhile the type can be represented as a class field; replace this field
469 // by a streamer field to read the data from disk.
470 auto substitute = std::make_unique<RStreamerField>(GetFieldName(), GetTypeName());
471 substitute->SetOnDiskId(GetOnDiskId());
472 return substitute;
473 }
474
475 for (auto linkId : fieldDesc.GetLinkIds()) {
476 const auto &subFieldDesc = desc.GetFieldDescriptor(linkId);
477 regularSubfields.insert(subFieldDesc.GetFieldName());
478 if (!subFieldDesc.GetFieldName().empty() && subFieldDesc.GetFieldName()[0] == ':')
480 }
481
482 rules = FindRules(&fieldDesc);
483
484 // 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
485 // (source) type name and version/checksum.
486 if (rules.empty()) {
487 // Otherwise we require compatible type names, after renormalization. GetTypeName() is already renormalized,
488 // but RNTuple data written with ROOT v6.34 might not have renormalized the field type name. Ask the
489 // RNTupleDescriptor, which knows about the spec version, for a fixed up type name.
491 if (GetTypeName() != descTypeName) {
492 throw RException(R__FAIL("incompatible type name for field " + GetFieldName() + ": " + GetTypeName() +
493 " vs. " + descTypeName));
494 }
495 }
496
497 if (!rules.empty()) {
498 SetStagingClass(fieldDesc.GetTypeName(), fieldDesc.GetTypeVersion());
499 PrepareStagingArea(rules, desc, fieldDesc);
500 for (auto &[_, si] : fStagingItems) {
502 si.fField = std::move(static_cast<RFieldZero *>(si.fField.get())->ReleaseSubfields()[0]);
503 }
504
505 // Remove target member of read rules from the list of regular members of the underlying on-disk field
506 for (const auto rule : rules) {
507 if (!rule->GetTarget())
508 continue;
509
510 for (const auto target : ROOT::Detail::TRangeStaticCast<const TObjString>(*rule->GetTarget())) {
511 regularSubfields.erase(std::string(target->GetString()));
512 }
513 }
514 }
515 }
516
517 for (const auto rule : rules) {
518 AddReadCallbacksFromIORule(rule);
519 }
520
521 // Iterate over all sub fields in memory and mark those as missing that are not in the descriptor.
522 int nInMemoryBaseClasses = 0;
523 for (auto &field : fSubfields) {
524 const auto &fieldName = field->GetFieldName();
525 if (regularSubfields.count(fieldName) == 0) {
526 CallSetArtificialOn(*field);
527 }
528 if (!fieldName.empty() && fieldName[0] == ':')
530 }
531
533 throw RException(R__FAIL(std::string("incompatible number of base classes for field ") + GetFieldName() + ": " +
534 GetTypeName() + ", " + std::to_string(nInMemoryBaseClasses) +
535 " base classes in memory "
536 " vs. " +
537 std::to_string(nOnDiskBaseClasses) + " base classes on-disk\n" +
538 Internal::GetTypeTraceReport(*this, pageSource.GetSharedDescriptorGuard().GetRef())));
539 }
540
541 return nullptr;
542}
543
545{
546 EnsureMatchingOnDiskField(desc, kDiffTypeVersion | kDiffTypeName).ThrowOnError();
547}
548
550{
551 fClass->New(where);
552}
553
555{
556 fClass->Destructor(objPtr, true /* dtorOnly */);
557 RDeleter::operator()(objPtr, dtorOnly);
558}
559
560std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue &value) const
561{
562 std::vector<RValue> result;
563 auto valuePtr = value.GetPtr<void>();
564 auto charPtr = static_cast<unsigned char *>(valuePtr.get());
565 result.reserve(fSubfields.size());
566 for (unsigned i = 0; i < fSubfields.size(); i++) {
567 result.emplace_back(
568 fSubfields[i]->BindValue(std::shared_ptr<void>(valuePtr, charPtr + fSubfieldsInfo[i].fOffset)));
569 }
570 return result;
571}
572
574{
575 return fClass->GetClassSize();
576}
577
579{
580 return fClass->GetClassVersion();
581}
582
584{
585 return fClass->GetCheckSum();
586}
587
588const std::type_info *ROOT::RClassField::GetPolymorphicTypeInfo() const
589{
590 bool polymorphic = fClass->ClassProperty() & kClassHasVirtual;
591 if (!polymorphic) {
592 return nullptr;
593 }
594 return fClass->GetTypeInfo();
595}
596
598{
599 visitor.VisitClassField(*this);
600}
601
602//------------------------------------------------------------------------------
603
604ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
606{
607}
608
610 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
611 false /* isSimple */)
612{
613 // Avoid accidentally supporting std types through TEnum.
614 if (enump->Property() & kIsDefinedInStd) {
615 throw RException(R__FAIL(GetTypeName() + " is not supported"));
616 }
617
618 switch (enump->GetUnderlyingType()) {
619 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
620 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
621 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
622 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
623 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
624 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
625 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
626 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
627 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
628 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
629 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
630 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
631 }
632
634}
635
636ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
637 std::unique_ptr<RFieldBase> intField)
639{
640 Attach(std::move(intField));
642}
643
644std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
645{
646 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
647 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
648}
649
651{
652 // TODO(jblomer): allow enum to enum conversion only by rename rule
653 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
654}
655
656std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
657{
658 std::vector<RValue> result;
659 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
660 return result;
661}
662
664{
665 visitor.VisitEnumField(*this);
666}
667
668//------------------------------------------------------------------------------
669
670std::string ROOT::RPairField::RPairField::GetTypeList(const std::array<std::unique_ptr<RFieldBase>, 2> &itemFields)
671{
672 return itemFields[0]->GetTypeName() + "," + itemFields[1]->GetTypeName();
673}
674
675ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields,
676 const std::array<std::size_t, 2> &offsets)
677 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
678{
679 AttachItemFields(std::move(itemFields));
680 fOffsets.push_back(offsets[0]);
681 fOffsets.push_back(offsets[1]);
682}
683
684ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
685 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
686{
687 AttachItemFields(std::move(itemFields));
688
689 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
690 auto *c = TClass::GetClass(GetTypeName().c_str());
691 if (!c)
692 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
693 fSize = c->Size();
694
695 auto firstElem = c->GetRealData("first");
696 if (!firstElem)
697 throw RException(R__FAIL("first: no such member"));
698 fOffsets.push_back(firstElem->GetThisOffset());
699
700 auto secondElem = c->GetRealData("second");
701 if (!secondElem)
702 throw RException(R__FAIL("second: no such member"));
703 fOffsets.push_back(secondElem->GetThisOffset());
704}
705
706std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
707{
708 std::array<std::size_t, 2> offsets = {fOffsets[0], fOffsets[1]};
709 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
710 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
711 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones), offsets));
712}
713
715{
716 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
717
718 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
719 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
720
721 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
722 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
723 if (nOnDiskSubfields != 2) {
724 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::pair " +
725 std::to_string(nOnDiskSubfields) + "\n" +
726 Internal::GetTypeTraceReport(*this, desc)));
727 }
728}
729
730//------------------------------------------------------------------------------
731
734 bool readFromDisk)
735{
737 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
738 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
739 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
740 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
741 (ifuncs.fNext != nullptr));
742 return ifuncs;
743}
744
746 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
747 false /* isSimple */),
748 fNWritten(0)
749{
750 if (!classp->GetCollectionProxy())
751 throw RException(R__FAIL(std::string(classp->GetName()) + " has no associated collection proxy"));
752 if (classp->Property() & kIsDefinedInStd) {
753 static const std::vector<std::string> supportedStdTypes = {
754 "std::set<", "std::unordered_set<", "std::multiset<", "std::unordered_multiset<",
755 "std::map<", "std::unordered_map<", "std::multimap<", "std::unordered_multimap<"};
756 bool isSupported = false;
757 for (const auto &tn : supportedStdTypes) {
758 if (GetTypeName().rfind(tn, 0) == 0) {
759 isSupported = true;
760 break;
761 }
762 }
763 if (!isSupported)
764 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
765 }
766
767 std::string renormalizedAlias;
770
771 fProxy.reset(classp->GetCollectionProxy()->Generate());
772 fProperties = fProxy->GetProperties();
773 fCollectionType = fProxy->GetCollectionType();
774 if (fProxy->HasPointers())
775 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
776
777 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
778 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
779}
780
781ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
783{
784 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
786 throw RException(R__FAIL("custom associative collection proxies not supported"));
787
788 std::unique_ptr<ROOT::RFieldBase> itemField;
789
790 if (auto valueClass = fProxy->GetValueClass()) {
791 // Element type is a class
792 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
793 } else {
794 switch (fProxy->GetType()) {
795 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
796 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
797 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
798 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
799 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
800 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
801 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
802 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
803 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
804 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
805 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
806 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
807 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
808 default: throw RException(R__FAIL("unsupported value type: " + std::to_string(fProxy->GetType())));
809 }
810 }
811
812 fItemSize = itemField->GetValueSize();
813 Attach(std::move(itemField));
814}
815
816std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
817{
818 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
819 auto clone =
820 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
821 clone->fItemSize = fItemSize;
822 clone->Attach(std::move(newItemField));
823 return clone;
824}
825
826std::size_t ROOT::RProxiedCollectionField::AppendImpl(const void *from)
827{
828 std::size_t nbytes = 0;
829 unsigned count = 0;
830 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
831 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
832 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
833 nbytes += CallAppendOn(*fSubfields[0], ptr);
834 count++;
835 }
836
837 fNWritten += count;
838 fPrincipalColumn->Append(&fNWritten);
839 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
840}
841
843{
846 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
847
848 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
849 void *obj =
850 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
851
852 unsigned i = 0;
853 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
854 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
855 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
856 }
857 if (obj != to)
858 fProxy->Commit(obj);
859}
860
870
875
880
882{
883 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
884}
885
887{
888 fProxy->New(where);
889}
890
891std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
892{
893 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
894 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
895 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
896 }
897 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
898}
899
901{
902 if (fItemDeleter) {
904 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
905 fItemDeleter->operator()(ptr, true /* dtorOnly */);
906 }
907 }
908 fProxy->Destructor(objPtr, true /* dtorOnly */);
909 RDeleter::operator()(objPtr, dtorOnly);
910}
911
912std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
913{
914 std::vector<RValue> result;
915 auto valueRawPtr = value.GetPtr<void>().get();
917 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
918 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
919 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
920 }
921 return result;
922}
923
925{
926 visitor.VisitProxiedCollectionField(*this);
927}
928
929//------------------------------------------------------------------------------
930
931ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
933{
934 auto *itemClass = fProxy->GetValueClass();
935 fItemSize = itemClass->GetClassSize();
936
937 Attach(std::move(itemField));
938}
939
940std::unique_ptr<ROOT::RFieldBase> ROOT::RMapField::CloneImpl(std::string_view newName) const
941{
942 return std::make_unique<RMapField>(newName, fMapType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
943}
944
946{
947 static const std::vector<std::string> prefixesRegular = {"std::map<", "std::unordered_map<"};
948
949 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
950
951 switch (fMapType) {
952 case EMapType::kMap:
953 case EMapType::kUnorderedMap: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
954 default:
955 break;
956 // no restrictions for multimaps
957 }
958}
959
960//------------------------------------------------------------------------------
961
962ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
964 fSetType(setType)
965{
966 fItemSize = itemField->GetValueSize();
967 Attach(std::move(itemField));
968}
969
970std::unique_ptr<ROOT::RFieldBase> ROOT::RSetField::CloneImpl(std::string_view newName) const
971{
972 return std::make_unique<RSetField>(newName, fSetType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
973}
974
976{
977 static const std::vector<std::string> prefixesRegular = {"std::set<", "std::unordered_set<", "std::map<",
978 "std::unordered_map<"};
979
980 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
981
982 switch (fSetType) {
983 case ESetType::kSet:
984 case ESetType::kUnorderedSet: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
985 default:
986 break;
987 // no restrictions for multisets
988 }
989}
990
991//------------------------------------------------------------------------------
992
993namespace {
994
995/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
996class TBufferRecStreamer : public TBufferFile {
997public:
998 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
999
1000private:
1001 RCallbackStreamerInfo fCallbackStreamerInfo;
1002
1003public:
1004 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
1005 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(callbackStreamerInfo)
1006 {
1007 }
1008 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
1009};
1010
1011} // anonymous namespace
1012
1013ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className)
1015{
1016}
1017
1019 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
1020 false /* isSimple */),
1021 fClass(classp),
1022 fIndex(0)
1023{
1024 std::string renormalizedAlias;
1027
1029 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
1030 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
1031 // implicit constructors and destructors.
1036}
1037
1038std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
1039{
1040 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, GetTypeName()));
1041}
1042
1043std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
1044{
1045 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
1046 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
1047 fClass->Streamer(const_cast<void *>(from), buffer);
1048
1049 auto nbytes = buffer.Length();
1050 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
1051 fIndex += nbytes;
1052 fPrincipalColumn->Append(&fIndex);
1053 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1054}
1055
1057{
1060 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
1061
1063 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
1064 fClass->Streamer(to, buffer);
1065}
1066
1076
1081
1086
1088{
1089 source.RegisterStreamerInfos();
1090 return nullptr;
1091}
1092
1094{
1095 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1096}
1097
1099{
1100 fClass->New(where);
1101}
1102
1104{
1105 fClass->Destructor(objPtr, true /* dtorOnly */);
1106 RDeleter::operator()(objPtr, dtorOnly);
1107}
1108
1110{
1113 .TypeVersion(GetTypeVersion())
1114 .TypeName(GetTypeName())
1116 return extraTypeInfoBuilder.MoveDescriptor().Unwrap();
1117}
1118
1120{
1121 return std::min(alignof(std::max_align_t), GetValueSize()); // TODO(jblomer): fix me
1122}
1123
1125{
1126 return fClass->GetClassSize();
1127}
1128
1130{
1131 return fClass->GetClassVersion();
1132}
1133
1135{
1136 return fClass->GetCheckSum();
1137}
1138
1140{
1141 visitor.VisitStreamerField(*this);
1142}
1143
1144//------------------------------------------------------------------------------
1145
1147{
1148 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1149 return dataMember->GetOffset();
1150 }
1151 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1152}
1153
1155 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1156{
1158 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1159 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1160}
1161
1163 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1164{
1165 assert(TObject::Class()->GetClassVersion() == 1);
1166
1168 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1169 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1170}
1171
1172std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1173{
1174 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1175}
1176
1177std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1178{
1179 // Cf. TObject::Streamer()
1180
1181 auto *obj = static_cast<const TObject *>(from);
1182 if (obj->TestBit(TObject::kIsReferenced)) {
1183 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1184 }
1185
1186 std::size_t nbytes = 0;
1187 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1188
1189 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1190 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1191 nbytes += CallAppendOn(*fSubfields[1], &bits);
1192
1193 return nbytes;
1194}
1195
1197{
1198 // Cf. TObject::Streamer()
1199
1200 auto *obj = static_cast<TObject *>(to);
1201 if (obj->TestBit(TObject::kIsReferenced)) {
1202 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1203 }
1204
1205 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1206
1207 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1209 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1210}
1211
1213{
1214 UInt_t uniqueID, bits;
1215 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1216 CallReadOn(*fSubfields[1], globalIndex, &bits);
1217 ReadTObject(to, uniqueID, bits);
1218}
1219
1221{
1222 UInt_t uniqueID, bits;
1223 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1224 CallReadOn(*fSubfields[1], localIndex, &bits);
1225 ReadTObject(to, uniqueID, bits);
1226}
1227
1229{
1230 return TObject::Class()->GetClassVersion();
1231}
1232
1234{
1235 return TObject::Class()->GetCheckSum();
1236}
1237
1239{
1240 new (where) TObject();
1241}
1242
1243std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1244{
1245 std::vector<RValue> result;
1246 // Use GetPtr<TObject> to type-check
1247 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1248 auto charPtr = static_cast<unsigned char *>(ptr.get());
1249 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1250 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1251 return result;
1252}
1253
1255{
1256 return sizeof(TObject);
1257}
1258
1260{
1261 return alignof(TObject);
1262}
1263
1265{
1266 visitor.VisitTObjectField(*this);
1267}
1268
1269//------------------------------------------------------------------------------
1270
1271std::string ROOT::RTupleField::RTupleField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
1272{
1273 std::string result;
1274 if (itemFields.empty())
1275 throw RException(R__FAIL("the type list for std::tuple must have at least one element"));
1276 for (size_t i = 0; i < itemFields.size(); ++i) {
1277 result += itemFields[i]->GetTypeName() + ",";
1278 }
1279 result.pop_back(); // remove trailing comma
1280 return result;
1281}
1282
1283ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields,
1284 const std::vector<std::size_t> &offsets)
1285 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
1286{
1287 AttachItemFields(std::move(itemFields));
1288 fOffsets = offsets;
1289}
1290
1291ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1292 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
1293{
1294 AttachItemFields(std::move(itemFields));
1295
1296 auto *c = TClass::GetClass(GetTypeName().c_str());
1297 if (!c)
1298 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1299 fSize = c->Size();
1300
1301 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1302 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1303 // following the order of the type list.
1304 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1305 // members, the assertion below will fail.
1306 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1307 std::string memberName("_" + std::to_string(i));
1308 auto member = c->GetRealData(memberName.c_str());
1309 if (!member)
1310 throw RException(R__FAIL(memberName + ": no such member"));
1311 fOffsets.push_back(member->GetThisOffset());
1312 }
1313}
1314
1315std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1316{
1317 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1318 itemClones.reserve(fSubfields.size());
1319 for (const auto &f : fSubfields) {
1320 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1321 }
1322 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones), fOffsets));
1323}
1324
1326{
1327 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1328
1329 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1330 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1331
1332 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1333 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1334 const auto nSubfields = fSubfields.size();
1336 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1337 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields) + "\n" +
1338 Internal::GetTypeTraceReport(*this, desc)));
1339 }
1340}
1341
1342//------------------------------------------------------------------------------
1343
1344namespace {
1345
1346// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1347constexpr std::size_t GetVariantTagSize()
1348{
1349 // Should be all zeros except for the tag, which is 1
1350 std::variant<char> t;
1351 constexpr auto sizeOfT = sizeof(t);
1352
1353 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1354 return sizeOfT == 2 ? 1 : 4;
1355}
1356
1357template <std::size_t VariantSizeT>
1358struct RVariantTag {
1359 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1360 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1361};
1362
1363} // anonymous namespace
1364
1365std::string ROOT::RVariantField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
1366{
1367 std::string result;
1368 for (size_t i = 0; i < itemFields.size(); ++i) {
1369 result += itemFields[i]->GetTypeName() + ",";
1370 }
1371 R__ASSERT(!result.empty()); // there is always at least one variant
1372 result.pop_back(); // remove trailing comma
1373 return result;
1374}
1375
1377 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1378 fMaxItemSize(source.fMaxItemSize),
1379 fMaxAlignment(source.fMaxAlignment),
1380 fTagOffset(source.fTagOffset),
1381 fVariantOffset(source.fVariantOffset),
1382 fNWritten(source.fNWritten.size(), 0)
1383{
1384 for (const auto &f : source.GetConstSubfields())
1385 Attach(f->Clone(f->GetFieldName()));
1386 fTraits = source.fTraits;
1387}
1388
1389ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1390 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields) + ">", ROOT::ENTupleStructure::kVariant,
1391 false /* isSimple */)
1392{
1393 // The variant needs to initialize its own tag member
1395
1396 auto nFields = itemFields.size();
1397 if (nFields == 0 || nFields > kMaxVariants) {
1398 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
1399 }
1400 fNWritten.resize(nFields, 0);
1401 for (unsigned int i = 0; i < nFields; ++i) {
1404 fTraits &= itemFields[i]->GetTraits();
1405 Attach(std::move(itemFields[i]));
1406 }
1407
1408 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
1409 // For instance, std::variant<std::optional<int>> on macOS.
1410 auto cl = TClass::GetClass(GetTypeName().c_str());
1411 assert(cl);
1412 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
1413 if (dm)
1414 fVariantOffset = dm->GetOffset();
1415
1416 const auto tagSize = GetVariantTagSize();
1417 const auto padding = tagSize - (fMaxItemSize % tagSize);
1419}
1420
1421std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
1422{
1423 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
1424}
1425
1426std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
1427{
1428 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1429 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
1430 return (tag == TagType_t(-1)) ? 0 : tag + 1;
1431}
1432
1433void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
1434{
1435 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1436 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
1437 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
1438}
1439
1440std::size_t ROOT::RVariantField::AppendImpl(const void *from)
1441{
1442 auto tag = GetTag(from, fTagOffset);
1443 std::size_t nbytes = 0;
1444 auto index = 0;
1445 if (tag > 0) {
1446 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
1447 index = fNWritten[tag - 1]++;
1448 }
1450 fPrincipalColumn->Append(&varSwitch);
1451 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
1452}
1453
1455{
1457 std::uint32_t tag;
1458 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
1459 R__ASSERT(tag < 256);
1460
1461 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
1462 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
1463 // any `std::holds_alternative<T>` check fail later.
1464 if (R__likely(tag > 0)) {
1465 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
1466 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
1467 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
1468 }
1469 SetTag(to, fTagOffset, tag);
1470}
1471
1477
1482
1487
1489{
1490 static const std::vector<std::string> prefixes = {"std::variant<"};
1491
1492 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1493 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1494
1495 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1496 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
1497 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName() + "\n" +
1498 Internal::GetTypeTraceReport(*this, desc)));
1499 }
1500}
1501
1503{
1504 memset(where, 0, GetValueSize());
1505 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
1506 SetTag(where, fTagOffset, 1);
1507}
1508
1510{
1511 auto tag = GetTag(objPtr, fTagOffset);
1512 if (tag > 0) {
1513 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
1514 }
1515 RDeleter::operator()(objPtr, dtorOnly);
1516}
1517
1518std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
1519{
1520 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
1521 itemDeleters.reserve(fSubfields.size());
1522 for (const auto &f : fSubfields) {
1523 itemDeleters.emplace_back(GetDeleterOf(*f));
1524 }
1525 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, std::move(itemDeleters));
1526}
1527
1529{
1530 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
1531}
1532
1534{
1535 const auto alignment = GetAlignment();
1536 const auto actualSize = fTagOffset + GetVariantTagSize();
1537 const auto padding = alignment - (actualSize % alignment);
1538 return actualSize + ((padding == alignment) ? 0 : padding);
1539}
1540
1542{
1543 std::fill(fNWritten.begin(), fNWritten.end(), 0);
1544}
Cppyy::TCppType_t fClass
#define R__likely(expr)
Definition RConfig.hxx:595
#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
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:134
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:222
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:163
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:152
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:289
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.
static void SetTypeAliasOf(RFieldBase &other, const std::string &alias)
Allow class fields to adjust the type alias of their members.
void Attach(std::unique_ptr< RFieldBase > child)
Add a new subfield to the list of nested fields.
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:49
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:319
RField(std::string_view name)
Definition RField.hxx:322
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(std::vector< std::unique_ptr< RFieldBase > > itemFields)
Definition RField.cxx:568
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:234
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.
static std::string GetTypeList(const std::vector< std::unique_ptr< RFieldBase > > &itemFields)
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:41
@ kIsOnHeap
object is on heap
Definition TObject.h:87
@ kNotDeleted
object has not been deleted
Definition TObject.h:88
static TClass * Class()
@ kIsReferenced
if object is referenced by a TRef or TRefArray
Definition TObject.h:71
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.