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