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
196 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
197 if (dataMember->Property() & kIsArray) {
198 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim) {
199 typeName += "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
200 }
201 }
202
203 auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
204
205 fTraits &= subField->GetTraits();
206 Attach(std::move(subField), RSubFieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
207 }
209}
210
212{
213 if (fStagingArea) {
214 for (const auto &[_, si] : fStagingItems) {
215 if (!(si.fField->GetTraits() & kTraitTriviallyDestructible)) {
216 auto deleter = GetDeleterOf(*si.fField);
217 deleter->operator()(fStagingArea.get() + si.fOffset, true /* dtorOnly */);
218 }
219 }
220 }
221}
222
223void ROOT::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubFieldInfo info)
224{
225 fMaxAlignment = std::max(fMaxAlignment, child->GetAlignment());
226 fSubfieldsInfo.push_back(info);
227 RFieldBase::Attach(std::move(child));
228}
229
230std::vector<const ROOT::TSchemaRule *> ROOT::RClassField::FindRules(const ROOT::RFieldDescriptor *fieldDesc)
231{
233 const auto ruleset = fClass->GetSchemaRules();
234 if (!ruleset)
235 return rules;
236
237 if (!fieldDesc) {
238 // If we have no on-disk information for the field, we still process the rules on the current in-memory version
239 // of the class
240 rules = ruleset->FindRules(fClass->GetName(), fClass->GetClassVersion(), fClass->GetCheckSum());
241 } else {
242 // We need to change (back) the name normalization from RNTuple to ROOT Meta
243 std::string normalizedName;
245 // We do have an on-disk field that correspond to the current RClassField instance. Ask for rules matching the
246 // on-disk version of the field.
247 if (fieldDesc->GetTypeChecksum()) {
248 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion(), *fieldDesc->GetTypeChecksum());
249 } else {
250 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion());
251 }
252 }
253
254 // Cleanup and sort rules
255 // Check that any any given source member uses the same type in all rules
256 std::unordered_map<std::string, std::string> sourceNameAndType;
257 std::size_t nskip = 0; // skip whole-object-rules that were moved to the end of the rules vector
258 for (auto itr = rules.begin(); itr != rules.end() - nskip;) {
259 const auto rule = *itr;
260
261 // Erase unknown rule types
262 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
264 << "ignoring I/O customization rule with unsupported type: " << rule->GetRuleType();
265 itr = rules.erase(itr);
266 continue;
267 }
268
269 bool hasConflictingSourceMembers = false;
270 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
271 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
272 auto [itrSrc, isNew] = sourceNameAndType.emplace(source->GetName(), memberType);
273 if (!isNew && (itrSrc->second != memberType)) {
275 << "ignoring I/O customization rule due to conflicting source member type: " << itrSrc->second << " vs. "
276 << memberType << " for member " << source->GetName();
278 break;
279 }
280 }
282 itr = rules.erase(itr);
283 continue;
284 }
285
286 // Rules targeting the entire object need to be executed at the end
287 if (rule->GetTarget() == nullptr) {
288 nskip++;
289 if (itr != rules.end() - nskip)
290 std::iter_swap(itr++, rules.end() - nskip);
291 continue;
292 }
293
294 ++itr;
295 }
296
297 return rules;
298}
299
300std::unique_ptr<ROOT::RFieldBase> ROOT::RClassField::CloneImpl(std::string_view newName) const
301{
302 return std::unique_ptr<RClassField>(new RClassField(newName, *this));
303}
304
305std::size_t ROOT::RClassField::AppendImpl(const void *from)
306{
307 std::size_t nbytes = 0;
308 for (unsigned i = 0; i < fSubfields.size(); i++) {
309 nbytes += CallAppendOn(*fSubfields[i], static_cast<const unsigned char *>(from) + fSubfieldsInfo[i].fOffset);
310 }
311 return nbytes;
312}
313
315{
316 for (const auto &[_, si] : fStagingItems) {
317 CallReadOn(*si.fField, globalIndex, fStagingArea.get() + si.fOffset);
318 }
319 for (unsigned i = 0; i < fSubfields.size(); i++) {
320 CallReadOn(*fSubfields[i], globalIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
321 }
322}
323
325{
326 for (const auto &[_, si] : fStagingItems) {
327 CallReadOn(*si.fField, localIndex, fStagingArea.get() + si.fOffset);
328 }
329 for (unsigned i = 0; i < fSubfields.size(); i++) {
330 CallReadOn(*fSubfields[i], localIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
331 }
332}
333
336{
339 return idSourceMember;
340
341 for (const auto &subFieldDesc : desc.GetFieldIterable(classFieldId)) {
342 const auto subFieldName = subFieldDesc.GetFieldName();
343 if (subFieldName.length() > 2 && subFieldName[0] == ':' && subFieldName[1] == '_') {
344 idSourceMember = LookupMember(desc, memberName, subFieldDesc.GetId());
346 return idSourceMember;
347 }
348 }
349
351}
352
353void ROOT::RClassField::SetStagingClass(const std::string &className, unsigned int classVersion)
354{
355 TClass::GetClass(className.c_str())->GetStreamerInfo(classVersion);
356 if (classVersion != GetTypeVersion() || className != GetTypeName()) {
357 fStagingClass = TClass::GetClass((className + std::string("@@") + std::to_string(classVersion)).c_str());
358 if (!fStagingClass) {
359 // For a rename rule, we may simply ask for the old class name
360 fStagingClass = TClass::GetClass(className.c_str());
361 }
362 } else {
363 fStagingClass = fClass;
364 }
365 R__ASSERT(fStagingClass);
366 R__ASSERT(static_cast<unsigned int>(fStagingClass->GetClassVersion()) == classVersion);
367}
368
369void ROOT::RClassField::PrepareStagingArea(const std::vector<const TSchemaRule *> &rules,
370 const ROOT::RNTupleDescriptor &desc,
372{
373 std::size_t stagingAreaSize = 0;
374 for (const auto rule : rules) {
375 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
376 auto [itr, isNew] = fStagingItems.emplace(source->GetName(), RStagingItem());
377 if (!isNew) {
378 // This source member has already been processed by another rule (and we only support one type per member)
379 continue;
380 }
381 RStagingItem &stagingItem = itr->second;
382
383 const auto memberFieldId = LookupMember(desc, source->GetName(), classFieldDesc.GetId());
385 throw RException(R__FAIL(std::string("cannot find on disk rule source member ") + GetTypeName() + "." +
386 source->GetName()));
387 }
388
389 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
390 auto memberField = Create("" /* we don't need a field name */, std::string(memberType)).Unwrap();
391 memberField->SetOnDiskId(memberFieldId);
392 auto fieldZero = std::make_unique<RFieldZero>();
394 fieldZero->Attach(std::move(memberField));
395 stagingItem.fField = std::move(fieldZero);
396
397 stagingItem.fOffset = fStagingClass->GetDataMemberOffset(source->GetName());
398 // Since we successfully looked up the source member in the RNTuple on-disk metadata, we expect it
399 // to be present in the TClass instance, too.
401 stagingAreaSize = std::max(stagingAreaSize, stagingItem.fOffset + stagingItem.fField->begin()->GetValueSize());
402 }
403 }
404
405 if (stagingAreaSize) {
406 R__ASSERT(static_cast<Int_t>(stagingAreaSize) <= fStagingClass->Size()); // we may have removed rules
407 // We use std::make_unique instead of MakeUninitArray to zero-initialize the staging area.
408 fStagingArea = std::make_unique<unsigned char[]>(stagingAreaSize);
409
410 for (const auto &[_, si] : fStagingItems) {
411 const auto &memberField = *si.fField->cbegin();
412 if (!(memberField.GetTraits() & kTraitTriviallyConstructible)) {
413 CallConstructValueOn(memberField, fStagingArea.get() + si.fOffset);
414 }
415 }
416 }
417}
418
420{
421 auto func = rule->GetReadFunctionPointer();
422 if (func == nullptr) {
423 // Can happen for rename rules
424 return;
425 }
426 fReadCallbacks.emplace_back([func, stagingClass = fStagingClass, stagingArea = fStagingArea.get()](void *target) {
427 TVirtualObject onfileObj{nullptr};
428 onfileObj.fClass = stagingClass;
429 onfileObj.fObject = stagingArea;
430 func(static_cast<char *>(target), &onfileObj);
431 onfileObj.fObject = nullptr; // TVirtualObject does not own the value
432 });
433}
434
436{
437 std::vector<const TSchemaRule *> rules;
438 // On-disk members that are not targeted by an I/O rule; all other sub fields of the in-memory class
439 // will be marked as artificial (added member in a new class version or member set by rule).
440 std::unordered_set<std::string> regularSubfields;
441 // We generally don't support changing the number of base classes, with the exception of changing from/to zero
442 // base classes. The variable stores the number of on-disk base classes.
443 int nOnDiskBaseClasses = 0;
444
445 if (GetOnDiskId() == kInvalidDescriptorId) {
446 // This can happen for added base classes or added members of class type
447 rules = FindRules(nullptr);
448 if (!rules.empty())
449 SetStagingClass(GetTypeName(), GetTypeVersion());
450 } else {
451 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
452 const ROOT::RNTupleDescriptor &desc = descriptorGuard.GetRef();
453 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
454
455 if (fieldDesc.GetStructure() == ENTupleStructure::kStreamer) {
456 // Streamer field on disk but meanwhile the type can be represented as a class field; replace this field
457 // by a streamer field to read the data from disk.
458 auto substitute = std::make_unique<RStreamerField>(GetFieldName(), GetTypeName());
459 substitute->SetOnDiskId(GetOnDiskId());
460 return substitute;
461 }
462
463 for (auto linkId : fieldDesc.GetLinkIds()) {
464 const auto &subFieldDesc = desc.GetFieldDescriptor(linkId);
465 regularSubfields.insert(subFieldDesc.GetFieldName());
466 if (!subFieldDesc.GetFieldName().empty() && subFieldDesc.GetFieldName()[0] == ':')
468 }
469
470 rules = FindRules(&fieldDesc);
471
472 // 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
473 // (source) type name and version/checksum.
474 if (rules.empty()) {
475 // Otherwise we require compatible type names, after renormalization. GetTypeName() is already renormalized,
476 // but RNTuple data written with ROOT v6.34 might not have renormalized the field type name. Ask the
477 // RNTupleDescriptor, which knows about the spec version, for a fixed up type name.
479 if (GetTypeName() != descTypeName) {
480 throw RException(R__FAIL("incompatible type name for field " + GetFieldName() + ": " + GetTypeName() +
481 " vs. " + descTypeName));
482 }
483 }
484
485 const bool hasSources = std::any_of(rules.begin(), rules.end(), [](const auto &r) {
486 return r->GetSource() && (r->GetSource()->GetEntries() > 0);
487 });
488
489 // A staging class (conversion streamer info) only exists if there is at least one rule that has an
490 // on disk source member defined.
491 if (hasSources) {
492 SetStagingClass(fieldDesc.GetTypeName(), fieldDesc.GetTypeVersion());
493 PrepareStagingArea(rules, desc, fieldDesc);
494 for (auto &[_, si] : fStagingItems) {
496 si.fField = std::move(static_cast<RFieldZero *>(si.fField.get())->ReleaseSubfields()[0]);
497 }
498 }
499
500 // Remove target member of read rules from the list of regular members of the underlying on-disk field
501 for (const auto rule : rules) {
502 if (!rule->GetTarget())
503 continue;
504
505 for (const auto target : ROOT::Detail::TRangeStaticCast<const TObjString>(*rule->GetTarget())) {
506 regularSubfields.erase(std::string(target->GetString()));
507 }
508 }
509 }
510
511 for (const auto rule : rules) {
512 AddReadCallbacksFromIORule(rule);
513 }
514
515 // Iterate over all sub fields in memory and mark those as missing that are not in the descriptor.
516 int nInMemoryBaseClasses = 0;
517 for (auto &field : fSubfields) {
518 const auto &fieldName = field->GetFieldName();
519 if (regularSubfields.count(fieldName) == 0) {
520 CallSetArtificialOn(*field);
521 }
522 if (!fieldName.empty() && fieldName[0] == ':')
524 }
525
527 throw RException(R__FAIL(std::string("incompatible number of base classes for field ") + GetFieldName() + ": " +
528 GetTypeName() + ", " + std::to_string(nInMemoryBaseClasses) +
529 " base classes in memory "
530 " vs. " +
531 std::to_string(nOnDiskBaseClasses) + " base classes on-disk\n" +
532 Internal::GetTypeTraceReport(*this, pageSource.GetSharedDescriptorGuard().GetRef())));
533 }
534
535 return nullptr;
536}
537
539{
540 EnsureMatchingOnDiskField(desc, kDiffTypeVersion | kDiffTypeName).ThrowOnError();
541}
542
544{
545 fClass->New(where);
546}
547
549{
550 fClass->Destructor(objPtr, true /* dtorOnly */);
551 RDeleter::operator()(objPtr, dtorOnly);
552}
553
554std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue &value) const
555{
556 std::vector<RValue> result;
557 auto valuePtr = value.GetPtr<void>();
558 auto charPtr = static_cast<unsigned char *>(valuePtr.get());
559 result.reserve(fSubfields.size());
560 for (unsigned i = 0; i < fSubfields.size(); i++) {
561 result.emplace_back(
562 fSubfields[i]->BindValue(std::shared_ptr<void>(valuePtr, charPtr + fSubfieldsInfo[i].fOffset)));
563 }
564 return result;
565}
566
568{
569 return fClass->GetClassSize();
570}
571
573{
574 return fClass->GetClassVersion();
575}
576
578{
579 return fClass->GetCheckSum();
580}
581
582const std::type_info *ROOT::RClassField::GetPolymorphicTypeInfo() const
583{
584 bool polymorphic = fClass->ClassProperty() & kClassHasVirtual;
585 if (!polymorphic) {
586 return nullptr;
587 }
588 return fClass->GetTypeInfo();
589}
590
592{
593 visitor.VisitClassField(*this);
594}
595
596//------------------------------------------------------------------------------
597
598ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
600{
601}
602
604 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
605 false /* isSimple */)
606{
607 // Avoid accidentally supporting std types through TEnum.
608 if (enump->Property() & kIsDefinedInStd) {
609 throw RException(R__FAIL(GetTypeName() + " is not supported"));
610 }
611
612 switch (enump->GetUnderlyingType()) {
613 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
614 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
615 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
616 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
617 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
618 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
619 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
620 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
621 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
622 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
623 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
624 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
625 }
626
628}
629
630ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
631 std::unique_ptr<RFieldBase> intField)
633{
634 Attach(std::move(intField));
636}
637
638std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
639{
640 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
641 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
642}
643
645{
646 // TODO(jblomer): allow enum to enum conversion only by rename rule
647 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
648}
649
650std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
651{
652 std::vector<RValue> result;
653 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
654 return result;
655}
656
658{
659 visitor.VisitEnumField(*this);
660}
661
662//------------------------------------------------------------------------------
663
664std::string ROOT::RPairField::RPairField::GetTypeList(const std::array<std::unique_ptr<RFieldBase>, 2> &itemFields)
665{
666 return itemFields[0]->GetTypeName() + "," + itemFields[1]->GetTypeName();
667}
668
669ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields,
670 const std::array<std::size_t, 2> &offsets)
671 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
672{
673 AttachItemFields(std::move(itemFields));
674 fOffsets.push_back(offsets[0]);
675 fOffsets.push_back(offsets[1]);
676}
677
678ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
679 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
680{
681 AttachItemFields(std::move(itemFields));
682
683 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
684 auto *c = TClass::GetClass(GetTypeName().c_str());
685 if (!c)
686 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
687 fSize = c->Size();
688
689 auto firstElem = c->GetRealData("first");
690 if (!firstElem)
691 throw RException(R__FAIL("first: no such member"));
692 fOffsets.push_back(firstElem->GetThisOffset());
693
694 auto secondElem = c->GetRealData("second");
695 if (!secondElem)
696 throw RException(R__FAIL("second: no such member"));
697 fOffsets.push_back(secondElem->GetThisOffset());
698}
699
700std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
701{
702 std::array<std::size_t, 2> offsets = {fOffsets[0], fOffsets[1]};
703 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
704 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
705 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones), offsets));
706}
707
709{
710 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
711
712 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
713 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
714
715 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
716 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
717 if (nOnDiskSubfields != 2) {
718 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::pair " +
719 std::to_string(nOnDiskSubfields) + "\n" +
720 Internal::GetTypeTraceReport(*this, desc)));
721 }
722}
723
724//------------------------------------------------------------------------------
725
728 bool readFromDisk)
729{
731 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
732 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
733 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
734 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
735 (ifuncs.fNext != nullptr));
736 return ifuncs;
737}
738
740 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
741 false /* isSimple */),
742 fNWritten(0)
743{
744 if (!classp->GetCollectionProxy())
745 throw RException(R__FAIL(std::string(classp->GetName()) + " has no associated collection proxy"));
746 if (classp->Property() & kIsDefinedInStd) {
747 static const std::vector<std::string> supportedStdTypes = {
748 "std::set<", "std::unordered_set<", "std::multiset<", "std::unordered_multiset<",
749 "std::map<", "std::unordered_map<", "std::multimap<", "std::unordered_multimap<"};
750 bool isSupported = false;
751 for (const auto &tn : supportedStdTypes) {
752 if (GetTypeName().rfind(tn, 0) == 0) {
753 isSupported = true;
754 break;
755 }
756 }
757 if (!isSupported)
758 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
759 }
760
761 std::string renormalizedAlias;
764
765 fProxy.reset(classp->GetCollectionProxy()->Generate());
766 fProperties = fProxy->GetProperties();
767 fCollectionType = fProxy->GetCollectionType();
768 if (fProxy->HasPointers())
769 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
770
771 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
772 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
773}
774
775ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
777{
778 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
780 throw RException(R__FAIL("custom associative collection proxies not supported"));
781
782 std::unique_ptr<ROOT::RFieldBase> itemField;
783
784 if (auto valueClass = fProxy->GetValueClass()) {
785 // Element type is a class
786 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
787 } else {
788 switch (fProxy->GetType()) {
789 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
790 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
791 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
792 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
793 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
794 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
795 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
796 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
797 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
798 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
799 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
800 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
801 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
802 default: throw RException(R__FAIL("unsupported value type: " + std::to_string(fProxy->GetType())));
803 }
804 }
805
806 fItemSize = itemField->GetValueSize();
807 Attach(std::move(itemField));
808}
809
810std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
811{
812 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
813 auto clone =
814 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
815 clone->fItemSize = fItemSize;
816 clone->Attach(std::move(newItemField));
817 return clone;
818}
819
820std::size_t ROOT::RProxiedCollectionField::AppendImpl(const void *from)
821{
822 std::size_t nbytes = 0;
823 unsigned count = 0;
824 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
825 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
826 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
827 nbytes += CallAppendOn(*fSubfields[0], ptr);
828 count++;
829 }
830
831 fNWritten += count;
832 fPrincipalColumn->Append(&fNWritten);
833 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
834}
835
837{
840 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
841
842 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
843 void *obj =
844 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
845
846 unsigned i = 0;
847 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
848 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
849 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
850 }
851 if (obj != to)
852 fProxy->Commit(obj);
853}
854
864
869
874
876{
877 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
878}
879
881{
882 fProxy->New(where);
883}
884
885std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
886{
887 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
888 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
889 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
890 }
891 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
892}
893
895{
896 if (fItemDeleter) {
898 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
899 fItemDeleter->operator()(ptr, true /* dtorOnly */);
900 }
901 }
902 fProxy->Destructor(objPtr, true /* dtorOnly */);
903 RDeleter::operator()(objPtr, dtorOnly);
904}
905
906std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
907{
908 std::vector<RValue> result;
909 auto valueRawPtr = value.GetPtr<void>().get();
911 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
912 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
913 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
914 }
915 return result;
916}
917
919{
920 visitor.VisitProxiedCollectionField(*this);
921}
922
923//------------------------------------------------------------------------------
924
925ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
927{
928 auto *itemClass = fProxy->GetValueClass();
929 fItemSize = itemClass->GetClassSize();
930
931 Attach(std::move(itemField));
932}
933
934std::unique_ptr<ROOT::RFieldBase> ROOT::RMapField::CloneImpl(std::string_view newName) const
935{
936 return std::make_unique<RMapField>(newName, fMapType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
937}
938
940{
941 static const std::vector<std::string> prefixesRegular = {"std::map<", "std::unordered_map<"};
942
943 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
944
945 switch (fMapType) {
946 case EMapType::kMap:
947 case EMapType::kUnorderedMap: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
948 default:
949 break;
950 // no restrictions for multimaps
951 }
952}
953
954//------------------------------------------------------------------------------
955
956ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
958 fSetType(setType)
959{
960 fItemSize = itemField->GetValueSize();
961 Attach(std::move(itemField));
962}
963
964std::unique_ptr<ROOT::RFieldBase> ROOT::RSetField::CloneImpl(std::string_view newName) const
965{
966 return std::make_unique<RSetField>(newName, fSetType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
967}
968
970{
971 static const std::vector<std::string> prefixesRegular = {"std::set<", "std::unordered_set<", "std::map<",
972 "std::unordered_map<"};
973
974 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
975
976 switch (fSetType) {
977 case ESetType::kSet:
978 case ESetType::kUnorderedSet: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
979 default:
980 break;
981 // no restrictions for multisets
982 }
983}
984
985//------------------------------------------------------------------------------
986
987namespace {
988
989/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
990class TBufferRecStreamer : public TBufferFile {
991public:
992 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
993
994private:
995 RCallbackStreamerInfo fCallbackStreamerInfo;
996
997public:
998 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
999 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(callbackStreamerInfo)
1000 {
1001 }
1002 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
1003};
1004
1005} // anonymous namespace
1006
1007ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className)
1009{
1010}
1011
1013 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
1014 false /* isSimple */),
1015 fClass(classp),
1016 fIndex(0)
1017{
1018 std::string renormalizedAlias;
1021
1023 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
1024 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
1025 // implicit constructors and destructors.
1030}
1031
1032std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
1033{
1034 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, GetTypeName()));
1035}
1036
1037std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
1038{
1039 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
1040 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
1041 fClass->Streamer(const_cast<void *>(from), buffer);
1042
1043 auto nbytes = buffer.Length();
1044 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
1045 fIndex += nbytes;
1046 fPrincipalColumn->Append(&fIndex);
1047 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1048}
1049
1051{
1054 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
1055
1057 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
1058 fClass->Streamer(to, buffer);
1059}
1060
1070
1075
1080
1082{
1083 source.RegisterStreamerInfos();
1084 return nullptr;
1085}
1086
1088{
1089 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1090}
1091
1093{
1094 fClass->New(where);
1095}
1096
1098{
1099 fClass->Destructor(objPtr, true /* dtorOnly */);
1100 RDeleter::operator()(objPtr, dtorOnly);
1101}
1102
1104{
1107 .TypeVersion(GetTypeVersion())
1108 .TypeName(GetTypeName())
1110 return extraTypeInfoBuilder.MoveDescriptor().Unwrap();
1111}
1112
1114{
1115 return std::min(alignof(std::max_align_t), GetValueSize()); // TODO(jblomer): fix me
1116}
1117
1119{
1120 return fClass->GetClassSize();
1121}
1122
1124{
1125 return fClass->GetClassVersion();
1126}
1127
1129{
1130 return fClass->GetCheckSum();
1131}
1132
1134{
1135 visitor.VisitStreamerField(*this);
1136}
1137
1138//------------------------------------------------------------------------------
1139
1141{
1142 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1143 return dataMember->GetOffset();
1144 }
1145 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1146}
1147
1149 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1150{
1152 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1153 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1154}
1155
1157 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1158{
1159 assert(TObject::Class()->GetClassVersion() == 1);
1160
1162 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1163 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1164}
1165
1166std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1167{
1168 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1169}
1170
1171std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1172{
1173 // Cf. TObject::Streamer()
1174
1175 auto *obj = static_cast<const TObject *>(from);
1176 if (obj->TestBit(TObject::kIsReferenced)) {
1177 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1178 }
1179
1180 std::size_t nbytes = 0;
1181 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1182
1183 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1184 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1185 nbytes += CallAppendOn(*fSubfields[1], &bits);
1186
1187 return nbytes;
1188}
1189
1191{
1192 // Cf. TObject::Streamer()
1193
1194 auto *obj = static_cast<TObject *>(to);
1195 if (obj->TestBit(TObject::kIsReferenced)) {
1196 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1197 }
1198
1199 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1200
1201 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1203 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1204}
1205
1207{
1208 UInt_t uniqueID, bits;
1209 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1210 CallReadOn(*fSubfields[1], globalIndex, &bits);
1211 ReadTObject(to, uniqueID, bits);
1212}
1213
1215{
1216 UInt_t uniqueID, bits;
1217 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1218 CallReadOn(*fSubfields[1], localIndex, &bits);
1219 ReadTObject(to, uniqueID, bits);
1220}
1221
1223{
1224 return TObject::Class()->GetClassVersion();
1225}
1226
1228{
1229 return TObject::Class()->GetCheckSum();
1230}
1231
1233{
1234 new (where) TObject();
1235}
1236
1237std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1238{
1239 std::vector<RValue> result;
1240 // Use GetPtr<TObject> to type-check
1241 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1242 auto charPtr = static_cast<unsigned char *>(ptr.get());
1243 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1244 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1245 return result;
1246}
1247
1249{
1250 return sizeof(TObject);
1251}
1252
1254{
1255 return alignof(TObject);
1256}
1257
1259{
1260 visitor.VisitTObjectField(*this);
1261}
1262
1263//------------------------------------------------------------------------------
1264
1265std::string ROOT::RTupleField::RTupleField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
1266{
1267 std::string result;
1268 if (itemFields.empty())
1269 throw RException(R__FAIL("the type list for std::tuple must have at least one element"));
1270 for (size_t i = 0; i < itemFields.size(); ++i) {
1271 result += itemFields[i]->GetTypeName() + ",";
1272 }
1273 result.pop_back(); // remove trailing comma
1274 return result;
1275}
1276
1277ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields,
1278 const std::vector<std::size_t> &offsets)
1279 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
1280{
1281 AttachItemFields(std::move(itemFields));
1282 fOffsets = offsets;
1283}
1284
1285ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1286 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
1287{
1288 AttachItemFields(std::move(itemFields));
1289
1290 auto *c = TClass::GetClass(GetTypeName().c_str());
1291 if (!c)
1292 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1293 fSize = c->Size();
1294
1295 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1296 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1297 // following the order of the type list.
1298 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1299 // members, the assertion below will fail.
1300 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1301 std::string memberName("_" + std::to_string(i));
1302 auto member = c->GetRealData(memberName.c_str());
1303 if (!member)
1304 throw RException(R__FAIL(memberName + ": no such member"));
1305 fOffsets.push_back(member->GetThisOffset());
1306 }
1307}
1308
1309std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1310{
1311 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1312 itemClones.reserve(fSubfields.size());
1313 for (const auto &f : fSubfields) {
1314 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1315 }
1316 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones), fOffsets));
1317}
1318
1320{
1321 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1322
1323 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1324 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1325
1326 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1327 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1328 const auto nSubfields = fSubfields.size();
1330 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1331 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields) + "\n" +
1332 Internal::GetTypeTraceReport(*this, desc)));
1333 }
1334}
1335
1336//------------------------------------------------------------------------------
1337
1338namespace {
1339
1340// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1341constexpr std::size_t GetVariantTagSize()
1342{
1343 // Should be all zeros except for the tag, which is 1
1344 std::variant<char> t;
1345 constexpr auto sizeOfT = sizeof(t);
1346
1347 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1348 return sizeOfT == 2 ? 1 : 4;
1349}
1350
1351template <std::size_t VariantSizeT>
1352struct RVariantTag {
1353 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1354 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1355};
1356
1357} // anonymous namespace
1358
1359std::string ROOT::RVariantField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
1360{
1361 std::string result;
1362 for (size_t i = 0; i < itemFields.size(); ++i) {
1363 result += itemFields[i]->GetTypeName() + ",";
1364 }
1365 R__ASSERT(!result.empty()); // there is always at least one variant
1366 result.pop_back(); // remove trailing comma
1367 return result;
1368}
1369
1371 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1372 fMaxItemSize(source.fMaxItemSize),
1373 fMaxAlignment(source.fMaxAlignment),
1374 fTagOffset(source.fTagOffset),
1375 fVariantOffset(source.fVariantOffset),
1376 fNWritten(source.fNWritten.size(), 0)
1377{
1378 for (const auto &f : source.GetConstSubfields())
1379 Attach(f->Clone(f->GetFieldName()));
1380 fTraits = source.fTraits;
1381}
1382
1383ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1384 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields) + ">", ROOT::ENTupleStructure::kVariant,
1385 false /* isSimple */)
1386{
1387 // The variant needs to initialize its own tag member
1389
1390 auto nFields = itemFields.size();
1391 if (nFields == 0 || nFields > kMaxVariants) {
1392 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
1393 }
1394 fNWritten.resize(nFields, 0);
1395 for (unsigned int i = 0; i < nFields; ++i) {
1398 fTraits &= itemFields[i]->GetTraits();
1399 Attach(std::move(itemFields[i]));
1400 }
1401
1402 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
1403 // For instance, std::variant<std::optional<int>> on macOS.
1404 auto cl = TClass::GetClass(GetTypeName().c_str());
1405 assert(cl);
1406 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
1407 if (dm)
1408 fVariantOffset = dm->GetOffset();
1409
1410 const auto tagSize = GetVariantTagSize();
1411 const auto padding = tagSize - (fMaxItemSize % tagSize);
1413}
1414
1415std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
1416{
1417 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
1418}
1419
1420std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
1421{
1422 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1423 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
1424 return (tag == TagType_t(-1)) ? 0 : tag + 1;
1425}
1426
1427void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
1428{
1429 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1430 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
1431 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
1432}
1433
1434std::size_t ROOT::RVariantField::AppendImpl(const void *from)
1435{
1436 auto tag = GetTag(from, fTagOffset);
1437 std::size_t nbytes = 0;
1438 auto index = 0;
1439 if (tag > 0) {
1440 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
1441 index = fNWritten[tag - 1]++;
1442 }
1444 fPrincipalColumn->Append(&varSwitch);
1445 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
1446}
1447
1449{
1451 std::uint32_t tag;
1452 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
1453 R__ASSERT(tag < 256);
1454
1455 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
1456 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
1457 // any `std::holds_alternative<T>` check fail later.
1458 if (R__likely(tag > 0)) {
1459 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
1460 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
1461 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
1462 }
1463 SetTag(to, fTagOffset, tag);
1464}
1465
1471
1476
1481
1483{
1484 static const std::vector<std::string> prefixes = {"std::variant<"};
1485
1486 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1487 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1488
1489 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1490 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
1491 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName() + "\n" +
1492 Internal::GetTypeTraceReport(*this, desc)));
1493 }
1494}
1495
1497{
1498 memset(where, 0, GetValueSize());
1499 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
1500 SetTag(where, fTagOffset, 1);
1501}
1502
1504{
1505 auto tag = GetTag(objPtr, fTagOffset);
1506 if (tag > 0) {
1507 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
1508 }
1509 RDeleter::operator()(objPtr, dtorOnly);
1510}
1511
1512std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
1513{
1514 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
1515 itemDeleters.reserve(fSubfields.size());
1516 for (const auto &f : fSubfields) {
1517 itemDeleters.emplace_back(GetDeleterOf(*f));
1518 }
1519 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, std::move(itemDeleters));
1520}
1521
1523{
1524 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
1525}
1526
1528{
1529 const auto alignment = GetAlignment();
1530 const auto actualSize = fTagOffset + GetVariantTagSize();
1531 const auto padding = alignment - (actualSize % alignment);
1532 return actualSize + ((padding == alignment) ? 0 : padding);
1533}
1534
1536{
1537 std::fill(fNWritten.begin(), fNWritten.end(), 0);
1538}
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 r
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.
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 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.