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