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 if (!rules.empty()) {
486 SetStagingClass(fieldDesc.GetTypeName(), fieldDesc.GetTypeVersion());
487 PrepareStagingArea(rules, desc, fieldDesc);
488 for (auto &[_, si] : fStagingItems) {
490 si.fField = std::move(static_cast<RFieldZero *>(si.fField.get())->ReleaseSubfields()[0]);
491 }
492
493 // Remove target member of read rules from the list of regular members of the underlying on-disk field
494 for (const auto rule : rules) {
495 if (!rule->GetTarget())
496 continue;
497
498 for (const auto target : ROOT::Detail::TRangeStaticCast<const TObjString>(*rule->GetTarget())) {
499 regularSubfields.erase(std::string(target->GetString()));
500 }
501 }
502 }
503 }
504
505 for (const auto rule : rules) {
506 AddReadCallbacksFromIORule(rule);
507 }
508
509 // Iterate over all sub fields in memory and mark those as missing that are not in the descriptor.
510 int nInMemoryBaseClasses = 0;
511 for (auto &field : fSubfields) {
512 const auto &fieldName = field->GetFieldName();
513 if (regularSubfields.count(fieldName) == 0) {
514 CallSetArtificialOn(*field);
515 }
516 if (!fieldName.empty() && fieldName[0] == ':')
518 }
519
521 throw RException(R__FAIL(std::string("incompatible number of base classes for field ") + GetFieldName() + ": " +
522 GetTypeName() + ", " + std::to_string(nInMemoryBaseClasses) +
523 " base classes in memory "
524 " vs. " +
525 std::to_string(nOnDiskBaseClasses) + " base classes on-disk\n" +
526 Internal::GetTypeTraceReport(*this, pageSource.GetSharedDescriptorGuard().GetRef())));
527 }
528
529 return nullptr;
530}
531
533{
534 EnsureMatchingOnDiskField(desc, kDiffTypeVersion | kDiffTypeName).ThrowOnError();
535}
536
538{
539 fClass->New(where);
540}
541
543{
544 fClass->Destructor(objPtr, true /* dtorOnly */);
545 RDeleter::operator()(objPtr, dtorOnly);
546}
547
548std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue &value) const
549{
550 std::vector<RValue> result;
551 auto valuePtr = value.GetPtr<void>();
552 auto charPtr = static_cast<unsigned char *>(valuePtr.get());
553 result.reserve(fSubfields.size());
554 for (unsigned i = 0; i < fSubfields.size(); i++) {
555 result.emplace_back(
556 fSubfields[i]->BindValue(std::shared_ptr<void>(valuePtr, charPtr + fSubfieldsInfo[i].fOffset)));
557 }
558 return result;
559}
560
562{
563 return fClass->GetClassSize();
564}
565
567{
568 return fClass->GetClassVersion();
569}
570
572{
573 return fClass->GetCheckSum();
574}
575
576const std::type_info *ROOT::RClassField::GetPolymorphicTypeInfo() const
577{
578 bool polymorphic = fClass->ClassProperty() & kClassHasVirtual;
579 if (!polymorphic) {
580 return nullptr;
581 }
582 return fClass->GetTypeInfo();
583}
584
586{
587 visitor.VisitClassField(*this);
588}
589
590//------------------------------------------------------------------------------
591
592ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
594{
595}
596
598 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
599 false /* isSimple */)
600{
601 // Avoid accidentally supporting std types through TEnum.
602 if (enump->Property() & kIsDefinedInStd) {
603 throw RException(R__FAIL(GetTypeName() + " is not supported"));
604 }
605
606 switch (enump->GetUnderlyingType()) {
607 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
608 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
609 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
610 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
611 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
612 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
613 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
614 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
615 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
616 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
617 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
618 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
619 }
620
622}
623
624ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
625 std::unique_ptr<RFieldBase> intField)
627{
628 Attach(std::move(intField));
630}
631
632std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
633{
634 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
635 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
636}
637
639{
640 // TODO(jblomer): allow enum to enum conversion only by rename rule
641 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
642}
643
644std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
645{
646 std::vector<RValue> result;
647 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
648 return result;
649}
650
652{
653 visitor.VisitEnumField(*this);
654}
655
656//------------------------------------------------------------------------------
657
658std::string ROOT::RPairField::RPairField::GetTypeList(const std::array<std::unique_ptr<RFieldBase>, 2> &itemFields)
659{
660 return itemFields[0]->GetTypeName() + "," + itemFields[1]->GetTypeName();
661}
662
663ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields,
664 const std::array<std::size_t, 2> &offsets)
665 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
666{
667 AttachItemFields(std::move(itemFields));
668 fOffsets.push_back(offsets[0]);
669 fOffsets.push_back(offsets[1]);
670}
671
672ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
673 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
674{
675 AttachItemFields(std::move(itemFields));
676
677 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
678 auto *c = TClass::GetClass(GetTypeName().c_str());
679 if (!c)
680 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
681 fSize = c->Size();
682
683 auto firstElem = c->GetRealData("first");
684 if (!firstElem)
685 throw RException(R__FAIL("first: no such member"));
686 fOffsets.push_back(firstElem->GetThisOffset());
687
688 auto secondElem = c->GetRealData("second");
689 if (!secondElem)
690 throw RException(R__FAIL("second: no such member"));
691 fOffsets.push_back(secondElem->GetThisOffset());
692}
693
694std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
695{
696 std::array<std::size_t, 2> offsets = {fOffsets[0], fOffsets[1]};
697 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
698 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
699 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones), offsets));
700}
701
703{
704 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
705
706 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
707 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
708
709 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
710 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
711 if (nOnDiskSubfields != 2) {
712 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::pair " +
713 std::to_string(nOnDiskSubfields) + "\n" +
714 Internal::GetTypeTraceReport(*this, desc)));
715 }
716}
717
718//------------------------------------------------------------------------------
719
722 bool readFromDisk)
723{
725 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
726 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
727 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
728 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
729 (ifuncs.fNext != nullptr));
730 return ifuncs;
731}
732
734 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
735 false /* isSimple */),
736 fNWritten(0)
737{
738 if (!classp->GetCollectionProxy())
739 throw RException(R__FAIL(std::string(classp->GetName()) + " has no associated collection proxy"));
740 if (classp->Property() & kIsDefinedInStd) {
741 static const std::vector<std::string> supportedStdTypes = {
742 "std::set<", "std::unordered_set<", "std::multiset<", "std::unordered_multiset<",
743 "std::map<", "std::unordered_map<", "std::multimap<", "std::unordered_multimap<"};
744 bool isSupported = false;
745 for (const auto &tn : supportedStdTypes) {
746 if (GetTypeName().rfind(tn, 0) == 0) {
747 isSupported = true;
748 break;
749 }
750 }
751 if (!isSupported)
752 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
753 }
754
755 std::string renormalizedAlias;
758
759 fProxy.reset(classp->GetCollectionProxy()->Generate());
760 fProperties = fProxy->GetProperties();
761 fCollectionType = fProxy->GetCollectionType();
762 if (fProxy->HasPointers())
763 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
764
765 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
766 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
767}
768
769ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
771{
772 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
774 throw RException(R__FAIL("custom associative collection proxies not supported"));
775
776 std::unique_ptr<ROOT::RFieldBase> itemField;
777
778 if (auto valueClass = fProxy->GetValueClass()) {
779 // Element type is a class
780 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
781 } else {
782 switch (fProxy->GetType()) {
783 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
784 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
785 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
786 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
787 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
788 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
789 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
790 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
791 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
792 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
793 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
794 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
795 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
796 default: throw RException(R__FAIL("unsupported value type: " + std::to_string(fProxy->GetType())));
797 }
798 }
799
800 fItemSize = itemField->GetValueSize();
801 Attach(std::move(itemField));
802}
803
804std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
805{
806 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
807 auto clone =
808 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
809 clone->fItemSize = fItemSize;
810 clone->Attach(std::move(newItemField));
811 return clone;
812}
813
814std::size_t ROOT::RProxiedCollectionField::AppendImpl(const void *from)
815{
816 std::size_t nbytes = 0;
817 unsigned count = 0;
818 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
819 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
820 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
821 nbytes += CallAppendOn(*fSubfields[0], ptr);
822 count++;
823 }
824
825 fNWritten += count;
826 fPrincipalColumn->Append(&fNWritten);
827 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
828}
829
831{
834 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
835
836 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
837 void *obj =
838 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
839
840 unsigned i = 0;
841 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
842 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
843 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
844 }
845 if (obj != to)
846 fProxy->Commit(obj);
847}
848
858
863
868
870{
871 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
872}
873
875{
876 fProxy->New(where);
877}
878
879std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
880{
881 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
882 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
883 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
884 }
885 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
886}
887
889{
890 if (fItemDeleter) {
892 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
893 fItemDeleter->operator()(ptr, true /* dtorOnly */);
894 }
895 }
896 fProxy->Destructor(objPtr, true /* dtorOnly */);
897 RDeleter::operator()(objPtr, dtorOnly);
898}
899
900std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
901{
902 std::vector<RValue> result;
903 auto valueRawPtr = value.GetPtr<void>().get();
905 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
906 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
907 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
908 }
909 return result;
910}
911
913{
914 visitor.VisitProxiedCollectionField(*this);
915}
916
917//------------------------------------------------------------------------------
918
919ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
921{
922 auto *itemClass = fProxy->GetValueClass();
923 fItemSize = itemClass->GetClassSize();
924
925 Attach(std::move(itemField));
926}
927
928std::unique_ptr<ROOT::RFieldBase> ROOT::RMapField::CloneImpl(std::string_view newName) const
929{
930 return std::make_unique<RMapField>(newName, fMapType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
931}
932
934{
935 static const std::vector<std::string> prefixesRegular = {"std::map<", "std::unordered_map<"};
936
937 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
938
939 switch (fMapType) {
940 case EMapType::kMap:
941 case EMapType::kUnorderedMap: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
942 default:
943 break;
944 // no restrictions for multimaps
945 }
946}
947
948//------------------------------------------------------------------------------
949
950ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
952 fSetType(setType)
953{
954 fItemSize = itemField->GetValueSize();
955 Attach(std::move(itemField));
956}
957
958std::unique_ptr<ROOT::RFieldBase> ROOT::RSetField::CloneImpl(std::string_view newName) const
959{
960 return std::make_unique<RSetField>(newName, fSetType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
961}
962
964{
965 static const std::vector<std::string> prefixesRegular = {"std::set<", "std::unordered_set<", "std::map<",
966 "std::unordered_map<"};
967
968 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
969
970 switch (fSetType) {
971 case ESetType::kSet:
972 case ESetType::kUnorderedSet: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
973 default:
974 break;
975 // no restrictions for multisets
976 }
977}
978
979//------------------------------------------------------------------------------
980
981namespace {
982
983/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
984class TBufferRecStreamer : public TBufferFile {
985public:
986 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
987
988private:
989 RCallbackStreamerInfo fCallbackStreamerInfo;
990
991public:
992 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
993 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(callbackStreamerInfo)
994 {
995 }
996 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
997};
998
999} // anonymous namespace
1000
1001ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className)
1003{
1004}
1005
1007 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
1008 false /* isSimple */),
1009 fClass(classp),
1010 fIndex(0)
1011{
1012 std::string renormalizedAlias;
1015
1017 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
1018 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
1019 // implicit constructors and destructors.
1024}
1025
1026std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
1027{
1028 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, GetTypeName()));
1029}
1030
1031std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
1032{
1033 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
1034 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
1035 fClass->Streamer(const_cast<void *>(from), buffer);
1036
1037 auto nbytes = buffer.Length();
1038 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
1039 fIndex += nbytes;
1040 fPrincipalColumn->Append(&fIndex);
1041 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1042}
1043
1045{
1048 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
1049
1051 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
1052 fClass->Streamer(to, buffer);
1053}
1054
1064
1069
1074
1076{
1077 source.RegisterStreamerInfos();
1078 return nullptr;
1079}
1080
1082{
1083 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1084}
1085
1087{
1088 fClass->New(where);
1089}
1090
1092{
1093 fClass->Destructor(objPtr, true /* dtorOnly */);
1094 RDeleter::operator()(objPtr, dtorOnly);
1095}
1096
1098{
1101 .TypeVersion(GetTypeVersion())
1102 .TypeName(GetTypeName())
1104 return extraTypeInfoBuilder.MoveDescriptor().Unwrap();
1105}
1106
1108{
1109 return std::min(alignof(std::max_align_t), GetValueSize()); // TODO(jblomer): fix me
1110}
1111
1113{
1114 return fClass->GetClassSize();
1115}
1116
1118{
1119 return fClass->GetClassVersion();
1120}
1121
1123{
1124 return fClass->GetCheckSum();
1125}
1126
1128{
1129 visitor.VisitStreamerField(*this);
1130}
1131
1132//------------------------------------------------------------------------------
1133
1135{
1136 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1137 return dataMember->GetOffset();
1138 }
1139 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1140}
1141
1143 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1144{
1146 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1147 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1148}
1149
1151 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1152{
1153 assert(TObject::Class()->GetClassVersion() == 1);
1154
1156 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1157 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1158}
1159
1160std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1161{
1162 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1163}
1164
1165std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1166{
1167 // Cf. TObject::Streamer()
1168
1169 auto *obj = static_cast<const TObject *>(from);
1170 if (obj->TestBit(TObject::kIsReferenced)) {
1171 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1172 }
1173
1174 std::size_t nbytes = 0;
1175 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1176
1177 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1178 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1179 nbytes += CallAppendOn(*fSubfields[1], &bits);
1180
1181 return nbytes;
1182}
1183
1185{
1186 // Cf. TObject::Streamer()
1187
1188 auto *obj = static_cast<TObject *>(to);
1189 if (obj->TestBit(TObject::kIsReferenced)) {
1190 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1191 }
1192
1193 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1194
1195 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1197 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1198}
1199
1201{
1202 UInt_t uniqueID, bits;
1203 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1204 CallReadOn(*fSubfields[1], globalIndex, &bits);
1205 ReadTObject(to, uniqueID, bits);
1206}
1207
1209{
1210 UInt_t uniqueID, bits;
1211 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1212 CallReadOn(*fSubfields[1], localIndex, &bits);
1213 ReadTObject(to, uniqueID, bits);
1214}
1215
1217{
1218 return TObject::Class()->GetClassVersion();
1219}
1220
1222{
1223 return TObject::Class()->GetCheckSum();
1224}
1225
1227{
1228 new (where) TObject();
1229}
1230
1231std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1232{
1233 std::vector<RValue> result;
1234 // Use GetPtr<TObject> to type-check
1235 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1236 auto charPtr = static_cast<unsigned char *>(ptr.get());
1237 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1238 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1239 return result;
1240}
1241
1243{
1244 return sizeof(TObject);
1245}
1246
1248{
1249 return alignof(TObject);
1250}
1251
1253{
1254 visitor.VisitTObjectField(*this);
1255}
1256
1257//------------------------------------------------------------------------------
1258
1259std::string ROOT::RTupleField::RTupleField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
1260{
1261 std::string result;
1262 if (itemFields.empty())
1263 throw RException(R__FAIL("the type list for std::tuple must have at least one element"));
1264 for (size_t i = 0; i < itemFields.size(); ++i) {
1265 result += itemFields[i]->GetTypeName() + ",";
1266 }
1267 result.pop_back(); // remove trailing comma
1268 return result;
1269}
1270
1271ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields,
1272 const std::vector<std::size_t> &offsets)
1273 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
1274{
1275 AttachItemFields(std::move(itemFields));
1276 fOffsets = offsets;
1277}
1278
1279ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1280 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
1281{
1282 AttachItemFields(std::move(itemFields));
1283
1284 auto *c = TClass::GetClass(GetTypeName().c_str());
1285 if (!c)
1286 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1287 fSize = c->Size();
1288
1289 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1290 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1291 // following the order of the type list.
1292 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1293 // members, the assertion below will fail.
1294 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1295 std::string memberName("_" + std::to_string(i));
1296 auto member = c->GetRealData(memberName.c_str());
1297 if (!member)
1298 throw RException(R__FAIL(memberName + ": no such member"));
1299 fOffsets.push_back(member->GetThisOffset());
1300 }
1301}
1302
1303std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1304{
1305 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1306 itemClones.reserve(fSubfields.size());
1307 for (const auto &f : fSubfields) {
1308 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1309 }
1310 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones), fOffsets));
1311}
1312
1314{
1315 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1316
1317 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1318 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1319
1320 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1321 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1322 const auto nSubfields = fSubfields.size();
1324 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1325 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields) + "\n" +
1326 Internal::GetTypeTraceReport(*this, desc)));
1327 }
1328}
1329
1330//------------------------------------------------------------------------------
1331
1332namespace {
1333
1334// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1335constexpr std::size_t GetVariantTagSize()
1336{
1337 // Should be all zeros except for the tag, which is 1
1338 std::variant<char> t;
1339 constexpr auto sizeOfT = sizeof(t);
1340
1341 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1342 return sizeOfT == 2 ? 1 : 4;
1343}
1344
1345template <std::size_t VariantSizeT>
1346struct RVariantTag {
1347 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1348 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1349};
1350
1351} // anonymous namespace
1352
1353std::string ROOT::RVariantField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
1354{
1355 std::string result;
1356 for (size_t i = 0; i < itemFields.size(); ++i) {
1357 result += itemFields[i]->GetTypeName() + ",";
1358 }
1359 R__ASSERT(!result.empty()); // there is always at least one variant
1360 result.pop_back(); // remove trailing comma
1361 return result;
1362}
1363
1365 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1366 fMaxItemSize(source.fMaxItemSize),
1367 fMaxAlignment(source.fMaxAlignment),
1368 fTagOffset(source.fTagOffset),
1369 fVariantOffset(source.fVariantOffset),
1370 fNWritten(source.fNWritten.size(), 0)
1371{
1372 for (const auto &f : source.GetConstSubfields())
1373 Attach(f->Clone(f->GetFieldName()));
1374 fTraits = source.fTraits;
1375}
1376
1377ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1378 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields) + ">", ROOT::ENTupleStructure::kVariant,
1379 false /* isSimple */)
1380{
1381 // The variant needs to initialize its own tag member
1383
1384 auto nFields = itemFields.size();
1385 if (nFields == 0 || nFields > kMaxVariants) {
1386 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
1387 }
1388 fNWritten.resize(nFields, 0);
1389 for (unsigned int i = 0; i < nFields; ++i) {
1392 fTraits &= itemFields[i]->GetTraits();
1393 Attach(std::move(itemFields[i]));
1394 }
1395
1396 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
1397 // For instance, std::variant<std::optional<int>> on macOS.
1398 auto cl = TClass::GetClass(GetTypeName().c_str());
1399 assert(cl);
1400 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
1401 if (dm)
1402 fVariantOffset = dm->GetOffset();
1403
1404 const auto tagSize = GetVariantTagSize();
1405 const auto padding = tagSize - (fMaxItemSize % tagSize);
1407}
1408
1409std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
1410{
1411 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
1412}
1413
1414std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
1415{
1416 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1417 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
1418 return (tag == TagType_t(-1)) ? 0 : tag + 1;
1419}
1420
1421void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
1422{
1423 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1424 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
1425 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
1426}
1427
1428std::size_t ROOT::RVariantField::AppendImpl(const void *from)
1429{
1430 auto tag = GetTag(from, fTagOffset);
1431 std::size_t nbytes = 0;
1432 auto index = 0;
1433 if (tag > 0) {
1434 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
1435 index = fNWritten[tag - 1]++;
1436 }
1438 fPrincipalColumn->Append(&varSwitch);
1439 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
1440}
1441
1443{
1445 std::uint32_t tag;
1446 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
1447 R__ASSERT(tag < 256);
1448
1449 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
1450 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
1451 // any `std::holds_alternative<T>` check fail later.
1452 if (R__likely(tag > 0)) {
1453 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
1454 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
1455 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
1456 }
1457 SetTag(to, fTagOffset, tag);
1458}
1459
1465
1470
1475
1477{
1478 static const std::vector<std::string> prefixes = {"std::variant<"};
1479
1480 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1481 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1482
1483 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1484 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
1485 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName() + "\n" +
1486 Internal::GetTypeTraceReport(*this, desc)));
1487 }
1488}
1489
1491{
1492 memset(where, 0, GetValueSize());
1493 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
1494 SetTag(where, fTagOffset, 1);
1495}
1496
1498{
1499 auto tag = GetTag(objPtr, fTagOffset);
1500 if (tag > 0) {
1501 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
1502 }
1503 RDeleter::operator()(objPtr, dtorOnly);
1504}
1505
1506std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
1507{
1508 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
1509 itemDeleters.reserve(fSubfields.size());
1510 for (const auto &f : fSubfields) {
1511 itemDeleters.emplace_back(GetDeleterOf(*f));
1512 }
1513 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, std::move(itemDeleters));
1514}
1515
1517{
1518 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
1519}
1520
1522{
1523 const auto alignment = GetAlignment();
1524 const auto actualSize = fTagOffset + GetVariantTagSize();
1525 const auto padding = alignment - (actualSize % alignment);
1526 return actualSize + ((padding == alignment) ? 0 : padding);
1527}
1528
1530{
1531 std::fill(fNWritten.begin(), fNWritten.end(), 0);
1532}
Cppyy::TCppType_t fClass
#define R__likely(expr)
Definition RConfig.hxx:595
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:300
#define R__LOG_WARNING(...)
Definition RLogger.hxx:358
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
@ kFloat_t
Definition TDataType.h:31
@ kULong64_t
Definition TDataType.h:32
@ kInt_t
Definition TDataType.h:30
@ kLong_t
Definition TDataType.h:30
@ kShort_t
Definition TDataType.h:29
@ kBool_t
Definition TDataType.h:32
@ kULong_t
Definition TDataType.h:30
@ kLong64_t
Definition TDataType.h:32
@ kUShort_t
Definition TDataType.h:29
@ kDouble_t
Definition TDataType.h:31
@ kChar_t
Definition TDataType.h:29
@ kUChar_t
Definition TDataType.h:29
@ kUInt_t
Definition TDataType.h:30
@ kClassHasExplicitCtor
@ kClassHasImplicitCtor
@ kClassHasVirtual
@ kClassHasExplicitDtor
@ kClassHasImplicitDtor
@ kIsArray
Definition TDictionary.h:79
@ kIsStatic
Definition TDictionary.h:80
@ kIsDefinedInStd
Definition TDictionary.h:98
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t child
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char mode
char name[80]
Definition TGX11.cxx:110
TCanvas * alignment()
Definition alignment.C:1
#define _(A, B)
Definition cfortran.h:108
Abstract base class for classes implementing the visitor design pattern.
Holds the index and the tag of a kSwitch column.
A helper class for piece-wise construction of an RExtraTypeInfoDescriptor.
static std::string SerializeStreamerInfos(const StreamerInfoMap_t &infos)
Abstract interface to read data from an ntuple.
void operator()(void *objPtr, bool dtorOnly) final
The field for a class with dictionary.
Definition RField.hxx:134
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
void AddReadCallbacksFromIORule(const TSchemaRule *rule)
Register post-read callback corresponding to a ROOT I/O customization rules.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.hxx:222
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void Attach(std::unique_ptr< RFieldBase > child, RSubFieldInfo info)
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
std::vector< const TSchemaRule * > FindRules(const ROOT::RFieldDescriptor *fieldDesc)
Given the on-disk information from the page source, find all the I/O customization rules that apply t...
ROOT::DescriptorId_t LookupMember(const ROOT::RNTupleDescriptor &desc, std::string_view memberName, ROOT::DescriptorId_t classFieldId)
Returns the id of member 'name' in the class field given by 'fieldId', or kInvalidDescriptorId if no ...
void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final
TClass * fClass
Definition RField.hxx:163
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
RClassField(std::string_view fieldName, const RClassField &source)
Used by CloneImpl.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
void PrepareStagingArea(const std::vector< const TSchemaRule * > &rules, const ROOT::RNTupleDescriptor &desc, const ROOT::RFieldDescriptor &classFieldId)
If there are rules with inputs (source members), create the staging area according to the TClass inst...
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
const std::type_info * GetPolymorphicTypeInfo() const
For polymorphic classes (that declare or inherit at least one virtual method), return the expected dy...
~RClassField() override
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
static constexpr const char * kPrefixInherited
Prefix used in the subfield names generated for base classes.
Definition RField.hxx:152
void SetStagingClass(const std::string &className, unsigned int classVersion)
Sets fStagingClass according to the given name and version.
The field for an unscoped or scoped enum with dictionary.
Definition RField.hxx:289
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
REnumField(std::string_view fieldName, TEnum *enump)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
Field specific extra type information from the header / extenstion header.
The list of column representations a field can have.
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
A field translates read and write calls from/to underlying columns to/from tree values.
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.