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
194
195 std::string renormalizedAlias;
198
199 int i = 0;
200 const auto *bases = fClass->GetListOfBases();
201 assert(bases);
203 if (baseClass->GetDelta() < 0) {
204 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
205 " virtually inherits from " + baseClass->GetName()));
206 }
207 TClass *c = baseClass->GetClassPointer();
208 auto subField =
209 RFieldBase::Create(std::string(kPrefixInherited) + "_" + std::to_string(i), c->GetName()).Unwrap();
210 fTraits &= subField->GetTraits();
211 Attach(std::move(subField), RSubFieldInfo{kBaseClass, static_cast<std::size_t>(baseClass->GetDelta())});
212 i++;
213 }
215 // Skip, for instance, unscoped enum constants defined in the class
216 if (dataMember->Property() & kIsStatic)
217 continue;
218 // Skip members explicitly marked as transient by user comment
219 if (!dataMember->IsPersistent()) {
220 // TODO(jblomer): we could do better
222 continue;
223 }
224
225 // NOTE: we use the already-resolved type name for the fields, otherwise TClass::GetClass may fail to resolve
226 // context-dependent types (e.g. typedefs defined in the class itself - which will not be fully qualified in
227 // the string returned by dataMember->GetFullTypeName())
228 std::string typeName{dataMember->GetTrueTypeName()};
229 // RFieldBase::Create() set subField->fTypeAlias based on the assumption that the user specified typeName, which
230 // already went through one round of type resolution.
231 std::string origTypeName{dataMember->GetFullTypeName()};
232
233 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
234 if (dataMember->Property() & kIsArray) {
235 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim) {
236 const auto addedStr = "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
237 typeName += addedStr;
239 }
240 }
241
242 auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
243
245 if (normTypeName == subField->GetTypeName()) {
247 } else {
249 }
250
251 fTraits &= subField->GetTraits();
252 Attach(std::move(subField), RSubFieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
253 }
255}
256
258{
259 if (fStagingArea) {
260 for (const auto &[_, si] : fStagingItems) {
261 if (!(si.fField->GetTraits() & kTraitTriviallyDestructible)) {
262 auto deleter = GetDeleterOf(*si.fField);
263 deleter->operator()(fStagingArea.get() + si.fOffset, true /* dtorOnly */);
264 }
265 }
266 }
267}
268
269void ROOT::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubFieldInfo info)
270{
271 fMaxAlignment = std::max(fMaxAlignment, child->GetAlignment());
272 fSubfieldsInfo.push_back(info);
273 RFieldBase::Attach(std::move(child));
274}
275
276std::vector<const ROOT::TSchemaRule *> ROOT::RClassField::FindRules(const ROOT::RFieldDescriptor *fieldDesc)
277{
279 const auto ruleset = fClass->GetSchemaRules();
280 if (!ruleset)
281 return rules;
282
283 if (!fieldDesc) {
284 // If we have no on-disk information for the field, we still process the rules on the current in-memory version
285 // of the class
286 rules = ruleset->FindRules(fClass->GetName(), fClass->GetClassVersion(), fClass->GetCheckSum());
287 } else {
288 // We need to change (back) the name normalization from RNTuple to ROOT Meta
289 std::string normalizedName;
291 // We do have an on-disk field that correspond to the current RClassField instance. Ask for rules matching the
292 // on-disk version of the field.
293 if (fieldDesc->GetTypeChecksum()) {
294 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion(), *fieldDesc->GetTypeChecksum());
295 } else {
296 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion());
297 }
298 }
299
300 // Cleanup and sort rules
301 // Check that any any given source member uses the same type in all rules
302 std::unordered_map<std::string, std::string> sourceNameAndType;
303 std::size_t nskip = 0; // skip whole-object-rules that were moved to the end of the rules vector
304 for (auto itr = rules.begin(); itr != rules.end() - nskip;) {
305 const auto rule = *itr;
306
307 // Erase unknown rule types
308 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
310 << "ignoring I/O customization rule with unsupported type: " << rule->GetRuleType();
311 itr = rules.erase(itr);
312 continue;
313 }
314
315 bool hasConflictingSourceMembers = false;
316 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
317 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
318 auto [itrSrc, isNew] = sourceNameAndType.emplace(source->GetName(), memberType);
319 if (!isNew && (itrSrc->second != memberType)) {
321 << "ignoring I/O customization rule due to conflicting source member type: " << itrSrc->second << " vs. "
322 << memberType << " for member " << source->GetName();
324 break;
325 }
326 }
328 itr = rules.erase(itr);
329 continue;
330 }
331
332 // Rules targeting the entire object need to be executed at the end
333 if (rule->GetTarget() == nullptr) {
334 nskip++;
335 if (itr != rules.end() - nskip)
336 std::iter_swap(itr++, rules.end() - nskip);
337 continue;
338 }
339
340 ++itr;
341 }
342
343 return rules;
344}
345
346std::unique_ptr<ROOT::RFieldBase> ROOT::RClassField::CloneImpl(std::string_view newName) const
347{
348 return std::unique_ptr<RClassField>(new RClassField(newName, *this));
349}
350
351std::size_t ROOT::RClassField::AppendImpl(const void *from)
352{
353 std::size_t nbytes = 0;
354 for (unsigned i = 0; i < fSubfields.size(); i++) {
355 nbytes += CallAppendOn(*fSubfields[i], static_cast<const unsigned char *>(from) + fSubfieldsInfo[i].fOffset);
356 }
357 return nbytes;
358}
359
361{
362 for (const auto &[_, si] : fStagingItems) {
363 CallReadOn(*si.fField, globalIndex, fStagingArea.get() + si.fOffset);
364 }
365 for (unsigned i = 0; i < fSubfields.size(); i++) {
366 CallReadOn(*fSubfields[i], globalIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
367 }
368}
369
371{
372 for (const auto &[_, si] : fStagingItems) {
373 CallReadOn(*si.fField, localIndex, fStagingArea.get() + si.fOffset);
374 }
375 for (unsigned i = 0; i < fSubfields.size(); i++) {
376 CallReadOn(*fSubfields[i], localIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
377 }
378}
379
382{
385 return idSourceMember;
386
387 for (const auto &subFieldDesc : desc.GetFieldIterable(classFieldId)) {
388 const auto subFieldName = subFieldDesc.GetFieldName();
389 if (subFieldName.length() > 2 && subFieldName[0] == ':' && subFieldName[1] == '_') {
390 idSourceMember = LookupMember(desc, memberName, subFieldDesc.GetId());
392 return idSourceMember;
393 }
394 }
395
397}
398
399void ROOT::RClassField::SetStagingClass(const std::string &className, unsigned int classVersion)
400{
401 TClass::GetClass(className.c_str())->GetStreamerInfo(classVersion);
402 if (classVersion != GetTypeVersion() || className != GetTypeName()) {
403 fStagingClass = TClass::GetClass((className + std::string("@@") + std::to_string(classVersion)).c_str());
404 if (!fStagingClass) {
405 // For a rename rule, we may simply ask for the old class name
406 fStagingClass = TClass::GetClass(className.c_str());
407 }
408 } else {
409 fStagingClass = fClass;
410 }
411 R__ASSERT(fStagingClass);
412 R__ASSERT(static_cast<unsigned int>(fStagingClass->GetClassVersion()) == classVersion);
413}
414
415void ROOT::RClassField::PrepareStagingArea(const std::vector<const TSchemaRule *> &rules,
416 const ROOT::RNTupleDescriptor &desc,
418{
419 std::size_t stagingAreaSize = 0;
420 for (const auto rule : rules) {
421 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
422 auto [itr, isNew] = fStagingItems.emplace(source->GetName(), RStagingItem());
423 if (!isNew) {
424 // This source member has already been processed by another rule (and we only support one type per member)
425 continue;
426 }
427 RStagingItem &stagingItem = itr->second;
428
429 const auto memberFieldId = LookupMember(desc, source->GetName(), classFieldDesc.GetId());
431 throw RException(R__FAIL(std::string("cannot find on disk rule source member ") + GetTypeName() + "." +
432 source->GetName()));
433 }
434
435 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
436 auto memberField = Create("" /* we don't need a field name */, std::string(memberType)).Unwrap();
437 memberField->SetOnDiskId(memberFieldId);
438 auto fieldZero = std::make_unique<RFieldZero>();
440 fieldZero->Attach(std::move(memberField));
441 stagingItem.fField = std::move(fieldZero);
442
443 stagingItem.fOffset = fStagingClass->GetDataMemberOffset(source->GetName());
444 // Since we successfully looked up the source member in the RNTuple on-disk metadata, we expect it
445 // to be present in the TClass instance, too.
447 stagingAreaSize = std::max(stagingAreaSize, stagingItem.fOffset + stagingItem.fField->begin()->GetValueSize());
448 }
449 }
450
451 if (stagingAreaSize) {
452 R__ASSERT(static_cast<Int_t>(stagingAreaSize) <= fStagingClass->Size()); // we may have removed rules
453 // We use std::make_unique instead of MakeUninitArray to zero-initialize the staging area.
454 fStagingArea = std::make_unique<unsigned char[]>(stagingAreaSize);
455
456 for (const auto &[_, si] : fStagingItems) {
457 const auto &memberField = *si.fField->cbegin();
458 if (!(memberField.GetTraits() & kTraitTriviallyConstructible)) {
459 CallConstructValueOn(memberField, fStagingArea.get() + si.fOffset);
460 }
461 }
462 }
463}
464
466{
467 auto func = rule->GetReadFunctionPointer();
468 if (func == nullptr) {
469 // Can happen for rename rules
470 return;
471 }
472 fReadCallbacks.emplace_back([func, stagingClass = fStagingClass, stagingArea = fStagingArea.get()](void *target) {
473 TVirtualObject onfileObj{nullptr};
474 onfileObj.fClass = stagingClass;
475 onfileObj.fObject = stagingArea;
476 func(static_cast<char *>(target), &onfileObj);
477 onfileObj.fObject = nullptr; // TVirtualObject does not own the value
478 });
479}
480
482{
483 std::vector<const TSchemaRule *> rules;
484 // On-disk members that are not targeted by an I/O rule; all other sub fields of the in-memory class
485 // will be marked as artificial (added member in a new class version or member set by rule).
486 std::unordered_set<std::string> regularSubfields;
487 // We generally don't support changing the number of base classes, with the exception of changing from/to zero
488 // base classes. The variable stores the number of on-disk base classes.
489 int nOnDiskBaseClasses = 0;
490
491 if (GetOnDiskId() == kInvalidDescriptorId) {
492 // This can happen for added base classes or added members of class type
493 rules = FindRules(nullptr);
494 if (!rules.empty())
495 SetStagingClass(GetTypeName(), GetTypeVersion());
496 } else {
497 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
498 const ROOT::RNTupleDescriptor &desc = descriptorGuard.GetRef();
499 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
500
501 if (fieldDesc.GetStructure() == ENTupleStructure::kStreamer) {
502 // Streamer field on disk but meanwhile the type can be represented as a class field; replace this field
503 // by a streamer field to read the data from disk.
504 auto substitute = std::make_unique<RStreamerField>(GetFieldName(), GetTypeName());
505 substitute->SetOnDiskId(GetOnDiskId());
506 return substitute;
507 }
508
509 for (auto linkId : fieldDesc.GetLinkIds()) {
510 const auto &subFieldDesc = desc.GetFieldDescriptor(linkId);
511 regularSubfields.insert(subFieldDesc.GetFieldName());
512 if (!subFieldDesc.GetFieldName().empty() && subFieldDesc.GetFieldName()[0] == ':')
514 }
515
516 rules = FindRules(&fieldDesc);
517
518 // 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
519 // (source) type name and version/checksum.
520 if (rules.empty()) {
521 // Otherwise we require compatible type names, after renormalization. GetTypeName() is already renormalized,
522 // but RNTuple data written with ROOT v6.34 might not have renormalized the field type name. Ask the
523 // RNTupleDescriptor, which knows about the spec version, for a fixed up type name.
525 if (GetTypeName() != descTypeName) {
526 throw RException(R__FAIL("incompatible type name for field " + GetFieldName() + ": " + GetTypeName() +
527 " vs. " + descTypeName));
528 }
529 }
530
531 if (!rules.empty()) {
532 SetStagingClass(fieldDesc.GetTypeName(), fieldDesc.GetTypeVersion());
533 PrepareStagingArea(rules, desc, fieldDesc);
534 for (auto &[_, si] : fStagingItems) {
536 si.fField = std::move(static_cast<RFieldZero *>(si.fField.get())->ReleaseSubfields()[0]);
537 }
538
539 // Remove target member of read rules from the list of regular members of the underlying on-disk field
540 for (const auto rule : rules) {
541 if (!rule->GetTarget())
542 continue;
543
544 for (const auto target : ROOT::Detail::TRangeStaticCast<const TObjString>(*rule->GetTarget())) {
545 regularSubfields.erase(std::string(target->GetString()));
546 }
547 }
548 }
549 }
550
551 for (const auto rule : rules) {
552 AddReadCallbacksFromIORule(rule);
553 }
554
555 // Iterate over all sub fields in memory and mark those as missing that are not in the descriptor.
556 int nInMemoryBaseClasses = 0;
557 for (auto &field : fSubfields) {
558 const auto &fieldName = field->GetFieldName();
559 if (regularSubfields.count(fieldName) == 0) {
560 CallSetArtificialOn(*field);
561 }
562 if (!fieldName.empty() && fieldName[0] == ':')
564 }
565
567 throw RException(R__FAIL(std::string("incompatible number of base classes for field ") + GetFieldName() + ": " +
568 GetTypeName() + ", " + std::to_string(nInMemoryBaseClasses) +
569 " base classes in memory "
570 " vs. " +
571 std::to_string(nOnDiskBaseClasses) + " base classes on-disk\n" +
572 Internal::GetTypeTraceReport(*this, pageSource.GetSharedDescriptorGuard().GetRef())));
573 }
574
575 return nullptr;
576}
577
579{
580 EnsureMatchingOnDiskField(desc, kDiffTypeVersion | kDiffTypeName).ThrowOnError();
581}
582
584{
585 fClass->New(where);
586}
587
589{
590 fClass->Destructor(objPtr, true /* dtorOnly */);
591 RDeleter::operator()(objPtr, dtorOnly);
592}
593
594std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue &value) const
595{
596 std::vector<RValue> result;
597 auto valuePtr = value.GetPtr<void>();
598 auto charPtr = static_cast<unsigned char *>(valuePtr.get());
599 result.reserve(fSubfields.size());
600 for (unsigned i = 0; i < fSubfields.size(); i++) {
601 result.emplace_back(
602 fSubfields[i]->BindValue(std::shared_ptr<void>(valuePtr, charPtr + fSubfieldsInfo[i].fOffset)));
603 }
604 return result;
605}
606
608{
609 return fClass->GetClassSize();
610}
611
613{
614 return fClass->GetClassVersion();
615}
616
618{
619 return fClass->GetCheckSum();
620}
621
622const std::type_info *ROOT::RClassField::GetPolymorphicTypeInfo() const
623{
624 bool polymorphic = fClass->ClassProperty() & kClassHasVirtual;
625 if (!polymorphic) {
626 return nullptr;
627 }
628 return fClass->GetTypeInfo();
629}
630
632{
633 visitor.VisitClassField(*this);
634}
635
636//------------------------------------------------------------------------------
637
638ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
640{
641}
642
644 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
645 false /* isSimple */)
646{
647 // Avoid accidentally supporting std types through TEnum.
648 if (enump->Property() & kIsDefinedInStd) {
649 throw RException(R__FAIL(GetTypeName() + " is not supported"));
650 }
651
652 switch (enump->GetUnderlyingType()) {
653 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
654 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
655 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
656 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
657 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
658 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
659 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
660 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
661 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
662 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
663 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
664 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
665 }
666
668}
669
670ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
671 std::unique_ptr<RFieldBase> intField)
673{
674 Attach(std::move(intField));
676}
677
678std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
679{
680 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
681 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
682}
683
685{
686 // TODO(jblomer): allow enum to enum conversion only by rename rule
687 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
688}
689
690std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
691{
692 std::vector<RValue> result;
693 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
694 return result;
695}
696
698{
699 visitor.VisitEnumField(*this);
700}
701
702//------------------------------------------------------------------------------
703
704ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields,
705 const std::array<std::size_t, 2> &offsets)
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 fOffsets.push_back(offsets[0]);
714 fOffsets.push_back(offsets[1]);
715}
716
717ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
718 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
719{
720 const std::string typeAlias = "std::pair<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
721 if (typeAlias != GetTypeName())
723
724 AttachItemFields(std::move(itemFields));
725
726 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
727 auto *c = TClass::GetClass(GetTypeName().c_str());
728 if (!c)
729 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
730 fSize = c->Size();
731
732 auto firstElem = c->GetRealData("first");
733 if (!firstElem)
734 throw RException(R__FAIL("first: no such member"));
735 fOffsets.push_back(firstElem->GetThisOffset());
736
737 auto secondElem = c->GetRealData("second");
738 if (!secondElem)
739 throw RException(R__FAIL("second: no such member"));
740 fOffsets.push_back(secondElem->GetThisOffset());
741}
742
743std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
744{
745 std::array<std::size_t, 2> offsets = {fOffsets[0], fOffsets[1]};
746 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
747 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
748 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones), offsets));
749}
750
752{
753 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
754
755 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
756 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
757
758 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
759 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
760 if (nOnDiskSubfields != 2) {
761 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::pair " +
762 std::to_string(nOnDiskSubfields) + "\n" +
763 Internal::GetTypeTraceReport(*this, desc)));
764 }
765}
766
767//------------------------------------------------------------------------------
768
771 bool readFromDisk)
772{
774 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
775 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
776 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
777 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
778 (ifuncs.fNext != nullptr));
779 return ifuncs;
780}
781
783 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
784 false /* isSimple */),
785 fNWritten(0)
786{
787 if (!classp->GetCollectionProxy())
788 throw RException(R__FAIL(std::string(classp->GetName()) + " has no associated collection proxy"));
789 if (classp->Property() & kIsDefinedInStd) {
790 static const std::vector<std::string> supportedStdTypes = {
791 "std::set<", "std::unordered_set<", "std::multiset<", "std::unordered_multiset<",
792 "std::map<", "std::unordered_map<", "std::multimap<", "std::unordered_multimap<"};
793 bool isSupported = false;
794 for (const auto &tn : supportedStdTypes) {
795 if (GetTypeName().rfind(tn, 0) == 0) {
796 isSupported = true;
797 break;
798 }
799 }
800 if (!isSupported)
801 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
802 }
803
804 std::string renormalizedAlias;
807
808 fProxy.reset(classp->GetCollectionProxy()->Generate());
809 fProperties = fProxy->GetProperties();
810 fCollectionType = fProxy->GetCollectionType();
811 if (fProxy->HasPointers())
812 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
813
814 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
815 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
816}
817
818ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
820{
821 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
823 throw RException(R__FAIL("custom associative collection proxies not supported"));
824
825 std::unique_ptr<ROOT::RFieldBase> itemField;
826
827 if (auto valueClass = fProxy->GetValueClass()) {
828 // Element type is a class
829 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
830 } else {
831 switch (fProxy->GetType()) {
832 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
833 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
834 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
835 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
836 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
837 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
838 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
839 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
840 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
841 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
842 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
843 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
844 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
845 default: throw RException(R__FAIL("unsupported value type: " + std::to_string(fProxy->GetType())));
846 }
847 }
848
849 fItemSize = itemField->GetValueSize();
850 Attach(std::move(itemField));
851}
852
853std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
854{
855 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
856 auto clone =
857 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
858 clone->fItemSize = fItemSize;
859 clone->Attach(std::move(newItemField));
860 return clone;
861}
862
863std::size_t ROOT::RProxiedCollectionField::AppendImpl(const void *from)
864{
865 std::size_t nbytes = 0;
866 unsigned count = 0;
867 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
868 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
869 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
870 nbytes += CallAppendOn(*fSubfields[0], ptr);
871 count++;
872 }
873
874 fNWritten += count;
875 fPrincipalColumn->Append(&fNWritten);
876 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
877}
878
880{
883 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
884
885 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
886 void *obj =
887 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
888
889 unsigned i = 0;
890 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
891 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
892 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
893 }
894 if (obj != to)
895 fProxy->Commit(obj);
896}
897
907
912
917
919{
920 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
921}
922
924{
925 fProxy->New(where);
926}
927
928std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
929{
930 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
931 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
932 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
933 }
934 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
935}
936
938{
939 if (fItemDeleter) {
941 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
942 fItemDeleter->operator()(ptr, true /* dtorOnly */);
943 }
944 }
945 fProxy->Destructor(objPtr, true /* dtorOnly */);
946 RDeleter::operator()(objPtr, dtorOnly);
947}
948
949std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
950{
951 std::vector<RValue> result;
952 auto valueRawPtr = value.GetPtr<void>().get();
954 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
955 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
956 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
957 }
958 return result;
959}
960
962{
963 visitor.VisitProxiedCollectionField(*this);
964}
965
966//------------------------------------------------------------------------------
967
968ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
970 EnsureValidClass(BuildMapTypeName(mapType, itemField.get(), false /* useTypeAliases */))),
971 fMapType(mapType)
972{
973 if (!itemField->GetTypeAlias().empty())
974 fTypeAlias = BuildMapTypeName(mapType, itemField.get(), true /* useTypeAliases */);
975
976 auto *itemClass = fProxy->GetValueClass();
977 fItemSize = itemClass->GetClassSize();
978
979 Attach(std::move(itemField));
980}
981
982std::unique_ptr<ROOT::RFieldBase> ROOT::RMapField::CloneImpl(std::string_view newName) const
983{
984 return std::make_unique<RMapField>(newName, fMapType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
985}
986
988{
989 static const std::vector<std::string> prefixesRegular = {"std::map<", "std::unordered_map<"};
990
991 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
992
993 switch (fMapType) {
994 case EMapType::kMap:
995 case EMapType::kUnorderedMap: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
996 default:
997 break;
998 // no restrictions for multimaps
999 }
1000}
1001
1002//------------------------------------------------------------------------------
1003
1004ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
1006 EnsureValidClass(BuildSetTypeName(setType, *itemField, false /* useTypeAlias */))),
1007 fSetType(setType)
1008{
1009 if (!itemField->GetTypeAlias().empty())
1010 fTypeAlias = BuildSetTypeName(setType, *itemField, true /* useTypeAlias */);
1011
1012 fItemSize = itemField->GetValueSize();
1013
1014 Attach(std::move(itemField));
1015}
1016
1017std::unique_ptr<ROOT::RFieldBase> ROOT::RSetField::CloneImpl(std::string_view newName) const
1018{
1019 return std::make_unique<RSetField>(newName, fSetType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1020}
1021
1023{
1024 static const std::vector<std::string> prefixesRegular = {"std::set<", "std::unordered_set<", "std::map<",
1025 "std::unordered_map<"};
1026
1027 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1028
1029 switch (fSetType) {
1030 case ESetType::kSet:
1031 case ESetType::kUnorderedSet: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1032 default:
1033 break;
1034 // no restrictions for multisets
1035 }
1036}
1037
1038//------------------------------------------------------------------------------
1039
1040namespace {
1041
1042/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
1043class TBufferRecStreamer : public TBufferFile {
1044public:
1045 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
1046
1047private:
1048 RCallbackStreamerInfo fCallbackStreamerInfo;
1049
1050public:
1051 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
1052 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(callbackStreamerInfo)
1053 {
1054 }
1055 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
1056};
1057
1058} // anonymous namespace
1059
1060ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className)
1062{
1063}
1064
1066 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
1067 false /* isSimple */),
1068 fClass(classp),
1069 fIndex(0)
1070{
1071 std::string renormalizedAlias;
1074
1076 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
1077 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
1078 // implicit constructors and destructors.
1083}
1084
1085std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
1086{
1087 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, GetTypeName()));
1088}
1089
1090std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
1091{
1092 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
1093 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
1094 fClass->Streamer(const_cast<void *>(from), buffer);
1095
1096 auto nbytes = buffer.Length();
1097 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
1098 fIndex += nbytes;
1099 fPrincipalColumn->Append(&fIndex);
1100 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1101}
1102
1104{
1107 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
1108
1110 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
1111 fClass->Streamer(to, buffer);
1112}
1113
1123
1128
1133
1135{
1136 source.RegisterStreamerInfos();
1137 return nullptr;
1138}
1139
1141{
1142 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1143}
1144
1146{
1147 fClass->New(where);
1148}
1149
1151{
1152 fClass->Destructor(objPtr, true /* dtorOnly */);
1153 RDeleter::operator()(objPtr, dtorOnly);
1154}
1155
1157{
1160 .TypeVersion(GetTypeVersion())
1161 .TypeName(GetTypeName())
1163 return extraTypeInfoBuilder.MoveDescriptor().Unwrap();
1164}
1165
1167{
1168 return std::min(alignof(std::max_align_t), GetValueSize()); // TODO(jblomer): fix me
1169}
1170
1172{
1173 return fClass->GetClassSize();
1174}
1175
1177{
1178 return fClass->GetClassVersion();
1179}
1180
1182{
1183 return fClass->GetCheckSum();
1184}
1185
1187{
1188 visitor.VisitStreamerField(*this);
1189}
1190
1191//------------------------------------------------------------------------------
1192
1194{
1195 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1196 return dataMember->GetOffset();
1197 }
1198 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1199}
1200
1202 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1203{
1205 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1206 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1207}
1208
1210 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1211{
1212 assert(TObject::Class()->GetClassVersion() == 1);
1213
1215 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1216 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1217}
1218
1219std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1220{
1221 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1222}
1223
1224std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1225{
1226 // Cf. TObject::Streamer()
1227
1228 auto *obj = static_cast<const TObject *>(from);
1229 if (obj->TestBit(TObject::kIsReferenced)) {
1230 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1231 }
1232
1233 std::size_t nbytes = 0;
1234 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1235
1236 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1237 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1238 nbytes += CallAppendOn(*fSubfields[1], &bits);
1239
1240 return nbytes;
1241}
1242
1244{
1245 // Cf. TObject::Streamer()
1246
1247 auto *obj = static_cast<TObject *>(to);
1248 if (obj->TestBit(TObject::kIsReferenced)) {
1249 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1250 }
1251
1252 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1253
1254 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1256 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1257}
1258
1260{
1261 UInt_t uniqueID, bits;
1262 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1263 CallReadOn(*fSubfields[1], globalIndex, &bits);
1264 ReadTObject(to, uniqueID, bits);
1265}
1266
1268{
1269 UInt_t uniqueID, bits;
1270 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1271 CallReadOn(*fSubfields[1], localIndex, &bits);
1272 ReadTObject(to, uniqueID, bits);
1273}
1274
1276{
1277 return TObject::Class()->GetClassVersion();
1278}
1279
1281{
1282 return TObject::Class()->GetCheckSum();
1283}
1284
1286{
1287 new (where) TObject();
1288}
1289
1290std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1291{
1292 std::vector<RValue> result;
1293 // Use GetPtr<TObject> to type-check
1294 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1295 auto charPtr = static_cast<unsigned char *>(ptr.get());
1296 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1297 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1298 return result;
1299}
1300
1302{
1303 return sizeof(TObject);
1304}
1305
1307{
1308 return alignof(TObject);
1309}
1310
1312{
1313 visitor.VisitTObjectField(*this);
1314}
1315
1316//------------------------------------------------------------------------------
1317
1318ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields,
1319 const std::vector<std::size_t> &offsets)
1320 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1321{
1322 const std::string typeAlias = "std::tuple<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1323 if (typeAlias != GetTypeName())
1325
1326 AttachItemFields(std::move(itemFields));
1327 fOffsets = offsets;
1328}
1329
1330ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1331 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1332{
1333 const std::string typeAlias = "std::tuple<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1334 if (typeAlias != GetTypeName())
1336
1337 AttachItemFields(std::move(itemFields));
1338
1339 auto *c = TClass::GetClass(GetTypeName().c_str());
1340 if (!c)
1341 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1342 fSize = c->Size();
1343
1344 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1345 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1346 // following the order of the type list.
1347 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1348 // members, the assertion below will fail.
1349 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1350 std::string memberName("_" + std::to_string(i));
1351 auto member = c->GetRealData(memberName.c_str());
1352 if (!member)
1353 throw RException(R__FAIL(memberName + ": no such member"));
1354 fOffsets.push_back(member->GetThisOffset());
1355 }
1356}
1357
1358std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1359{
1360 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1361 itemClones.reserve(fSubfields.size());
1362 for (const auto &f : fSubfields) {
1363 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1364 }
1365 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones), fOffsets));
1366}
1367
1369{
1370 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1371
1372 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1373 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1374
1375 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1376 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1377 const auto nSubfields = fSubfields.size();
1379 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1380 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields) + "\n" +
1381 Internal::GetTypeTraceReport(*this, desc)));
1382 }
1383}
1384
1385//------------------------------------------------------------------------------
1386
1387namespace {
1388
1389// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1390constexpr std::size_t GetVariantTagSize()
1391{
1392 // Should be all zeros except for the tag, which is 1
1393 std::variant<char> t;
1394 constexpr auto sizeOfT = sizeof(t);
1395
1396 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1397 return sizeOfT == 2 ? 1 : 4;
1398}
1399
1400template <std::size_t VariantSizeT>
1401struct RVariantTag {
1402 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1403 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1404};
1405
1406} // anonymous namespace
1407
1409 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1410 fMaxItemSize(source.fMaxItemSize),
1411 fMaxAlignment(source.fMaxAlignment),
1412 fTagOffset(source.fTagOffset),
1413 fVariantOffset(source.fVariantOffset),
1414 fNWritten(source.fNWritten.size(), 0)
1415{
1416 for (const auto &f : source.GetConstSubfields())
1417 Attach(f->Clone(f->GetFieldName()));
1418 fTraits = source.fTraits;
1419}
1420
1421ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1422 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">",
1423 ROOT::ENTupleStructure::kVariant, false /* isSimple */)
1424{
1425 // The variant needs to initialize its own tag member
1427
1428 const std::string typeAlias = "std::variant<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1429 if (typeAlias != GetTypeName())
1431
1432 auto nFields = itemFields.size();
1433 if (nFields == 0 || nFields > kMaxVariants) {
1434 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
1435 }
1436 fNWritten.resize(nFields, 0);
1437 for (unsigned int i = 0; i < nFields; ++i) {
1440 fTraits &= itemFields[i]->GetTraits();
1441 Attach(std::move(itemFields[i]));
1442 }
1443
1444 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
1445 // For instance, std::variant<std::optional<int>> on macOS.
1446 auto cl = TClass::GetClass(GetTypeName().c_str());
1447 assert(cl);
1448 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
1449 if (dm)
1450 fVariantOffset = dm->GetOffset();
1451
1452 const auto tagSize = GetVariantTagSize();
1453 const auto padding = tagSize - (fMaxItemSize % tagSize);
1455}
1456
1457std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
1458{
1459 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
1460}
1461
1462std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
1463{
1464 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1465 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
1466 return (tag == TagType_t(-1)) ? 0 : tag + 1;
1467}
1468
1469void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
1470{
1471 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1472 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
1473 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
1474}
1475
1476std::size_t ROOT::RVariantField::AppendImpl(const void *from)
1477{
1478 auto tag = GetTag(from, fTagOffset);
1479 std::size_t nbytes = 0;
1480 auto index = 0;
1481 if (tag > 0) {
1482 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
1483 index = fNWritten[tag - 1]++;
1484 }
1486 fPrincipalColumn->Append(&varSwitch);
1487 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
1488}
1489
1491{
1493 std::uint32_t tag;
1494 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
1495 R__ASSERT(tag < 256);
1496
1497 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
1498 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
1499 // any `std::holds_alternative<T>` check fail later.
1500 if (R__likely(tag > 0)) {
1501 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
1502 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
1503 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
1504 }
1505 SetTag(to, fTagOffset, tag);
1506}
1507
1513
1518
1523
1525{
1526 static const std::vector<std::string> prefixes = {"std::variant<"};
1527
1528 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1529 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1530
1531 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1532 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
1533 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName() + "\n" +
1534 Internal::GetTypeTraceReport(*this, desc)));
1535 }
1536}
1537
1539{
1540 memset(where, 0, GetValueSize());
1541 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
1542 SetTag(where, fTagOffset, 1);
1543}
1544
1546{
1547 auto tag = GetTag(objPtr, fTagOffset);
1548 if (tag > 0) {
1549 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
1550 }
1551 RDeleter::operator()(objPtr, dtorOnly);
1552}
1553
1554std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
1555{
1556 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
1557 itemDeleters.reserve(fSubfields.size());
1558 for (const auto &f : fSubfields) {
1559 itemDeleters.emplace_back(GetDeleterOf(*f));
1560 }
1561 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, std::move(itemDeleters));
1562}
1563
1565{
1566 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
1567}
1568
1570{
1571 const auto alignment = GetAlignment();
1572 const auto actualSize = fTagOffset + GetVariantTagSize();
1573 const auto padding = alignment - (actualSize % alignment);
1574 return actualSize + ((padding == alignment) ? 0 : padding);
1575}
1576
1578{
1579 std::fill(fNWritten.begin(), fNWritten.end(), 0);
1580}
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
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:134
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
void AddReadCallbacksFromIORule(const TSchemaRule *rule)
Register post-read callback corresponding to a ROOT I/O customization rules.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.hxx:222
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void Attach(std::unique_ptr< RFieldBase > child, RSubFieldInfo info)
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
std::vector< const TSchemaRule * > FindRules(const ROOT::RFieldDescriptor *fieldDesc)
Given the on-disk information from the page source, find all the I/O customization rules that apply t...
ROOT::DescriptorId_t LookupMember(const ROOT::RNTupleDescriptor &desc, std::string_view memberName, ROOT::DescriptorId_t classFieldId)
Returns the id of member 'name' in the class field given by 'fieldId', or kInvalidDescriptorId if no ...
void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final
TClass * fClass
Definition RField.hxx:163
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
RClassField(std::string_view fieldName, const RClassField &source)
Used by CloneImpl.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
void PrepareStagingArea(const std::vector< const TSchemaRule * > &rules, const ROOT::RNTupleDescriptor &desc, const ROOT::RFieldDescriptor &classFieldId)
If there are rules with inputs (source members), create the staging area according to the TClass inst...
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
const std::type_info * GetPolymorphicTypeInfo() const
For polymorphic classes (that declare or inherit at least one virtual method), return the expected dy...
~RClassField() override
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
static constexpr const char * kPrefixInherited
Prefix used in the subfield names generated for base classes.
Definition RField.hxx:152
void SetStagingClass(const std::string &className, unsigned int classVersion)
Sets fStagingClass according to the given name and version.
The field for an unscoped or scoped enum with dictionary.
Definition RField.hxx:289
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
REnumField(std::string_view fieldName, TEnum *enump)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
Field specific extra type information from the header / extenstion header.
The list of column representations a field can have.
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
A field translates read and write calls from/to underlying columns to/from tree values.
static void SetTypeAliasOf(RFieldBase &other, const std::string &alias)
Allow class fields to adjust the type alias of their members.
void Attach(std::unique_ptr< RFieldBase > child)
Add a new subfield to the list of nested fields.
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
std::uint32_t fTraits
Properties of the type that allow for optimizations of collections of that type.
static RResult< std::unique_ptr< RFieldBase > > Create(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
Factory method to resurrect a field from the stored on-disk type information.
std::string fTypeAlias
A typedef or using name that was used when creating the field.
const std::string & GetTypeName() const
@ kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
@ kTraitTriviallyConstructible
No constructor needs to be called, i.e.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
Metadata stored for every field of an RNTuple.
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:59
std::vector< std::unique_ptr< RFieldBase > > ReleaseSubfields()
Moves all subfields into the returned vector.
Definition RField.cxx:49
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:319
RField(std::string_view name)
Definition RField.hxx:322
RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr< RFieldBase > itemField)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
The on-storage metadata of an RNTuple.
RFieldDescriptorIterable GetFieldIterable(const RFieldDescriptor &fieldDesc) const
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
Template specializations for C++ std::pair.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RPairField(std::string_view fieldName, std::array< std::unique_ptr< RFieldBase >, 2 > itemFields, const std::array< std::size_t, 2 > &offsets)
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Allows for iterating over the elements of a proxied collection.
static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk)
void operator()(void *objPtr, bool dtorOnly) final
The field for a class representing a collection of elements via TVirtualCollectionProxy.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
RProxiedCollectionField(std::string_view fieldName, TClass *classp)
Constructor used when the value type of the collection is not known in advance, i....
RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RCollectionIterableOnce::RIteratorFuncs fIFuncsRead
Two sets of functions to operate on iterators, to be used depending on the access type.
std::shared_ptr< TVirtualCollectionProxy > fProxy
The collection proxy is needed by the deleters and thus defined as a shared pointer.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) override
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
const_iterator begin() const
const_iterator end() const
The field for an untyped record.
void AttachItemFields(std::vector< std::unique_ptr< RFieldBase > > itemFields)
Definition RField.cxx:568
std::vector< std::size_t > fOffsets
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr< RFieldBase > itemField)
void operator()(void *objPtr, bool dtorOnly) final
The field for a class using ROOT standard streaming.
Definition RField.hxx:234
ROOT::RExtraTypeInfoDescriptor GetExtraTypeInfo() const final
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &source) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RStreamerField(std::string_view fieldName, TClass *classp)
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
Template specializations for C++ std::tuple.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RTupleField(std::string_view fieldName, std::vector< std::unique_ptr< RFieldBase > > itemFields, const std::vector< std::size_t > &offsets)
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void operator()(void *objPtr, bool dtorOnly) final
Template specializations for C++ std::variant.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
static constexpr std::size_t kMaxVariants
std::vector< ROOT::Internal::RColumnIndex::ValueType > fNWritten
static std::uint8_t GetTag(const void *variantPtr, std::size_t tagOffset)
Extracts the index from an std::variant and transforms it into the 1-based index used for the switch ...
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
size_t fVariantOffset
In the std::variant memory layout, the actual union of types may start at an offset > 0.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
size_t fTagOffset
In the std::variant memory layout, at which byte number is the index stored.
RVariantField(std::string_view name, const RVariantField &source)
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
static void SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
void CommitClusterImpl() final
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
Definition TBufferFile.h:47
@ kWrite
Definition TBuffer.h:73
@ kRead
Definition TBuffer.h:73
char * Buffer() const
Definition TBuffer.h:96
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
Bool_t CanSplit() const
Return true if the data member of this TClass can be saved separately.
Definition TClass.cxx:2324
EState GetState() const
Definition TClass.h:501
TList * GetListOfDataMembers(Bool_t load=kTRUE)
Return list containing the TDataMembers of a class.
Definition TClass.cxx:3797
Int_t Size() const
Return size of object of this class.
Definition TClass.cxx:5743
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition TClass.cxx:3663
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2902
Long_t ClassProperty() const
Return the C++ property of this class, eg.
Definition TClass.cxx:2401
Long_t Property() const override
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition TClass.cxx:6128
@ kInterpreted
Definition TClass.h:129
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2973
All ROOT classes may have RTTI (run time type identification) support added.
Definition TDataMember.h:31
The TEnum class implements the enum type.
Definition TEnum.h:33
static TEnum * GetEnum(const std::type_info &ti, ESearchAction sa=kALoadAndInterpLookup)
Definition TEnum.cxx:181
Mother of all ROOT objects.
Definition TObject.h:41
@ kIsOnHeap
object is on heap
Definition TObject.h:87
@ kNotDeleted
object has not been deleted
Definition TObject.h:88
static TClass * Class()
@ kIsReferenced
if object is referenced by a TRef or TRefArray
Definition TObject.h:71
RAII helper class that ensures that PushProxy() / PopProxy() are called when entering / leaving a C++...
Defines a common interface to inspect/change the contents of an object that represents a collection.
@ kNeedDelete
The collection contains directly or indirectly (via other collection) some pointers that need explici...
Abstract Interface class describing Streamer information for one class.
const Int_t n
Definition legend1.C:16
void SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val)
Definition RField.cxx:36
ROOT::RLogChannel & NTupleLog()
Log channel for RNTuple diagnostics.
void CallConnectPageSourceOnField(RFieldBase &, ROOT::Internal::RPageSource &)
bool NeedsMetaNameAsAlias(const std::string &metaNormalizedName, std::string &renormalizedAlias, bool isArgInTemplatedUserClass=false)
Checks if the meta normalized name is different from the RNTuple normalized name in a way that would ...
ERNTupleSerializationMode GetRNTupleSerializationMode(TClass *cl)
std::string GetTypeTraceReport(const RFieldBase &field, const RNTupleDescriptor &desc)
Prints the hierarchy of types with their field names and field IDs for the given in-memory field and ...
std::string GetNormalizedUnresolvedTypeName(const std::string &origName)
Applies all RNTuple type normalization rules except typedef resolution.
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
@ kSTLvector
Definition ESTLType.h:30
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
ENTupleStructure
The fields in the RNTuple data model tree can carry different structural information about the type s...
void GetNormalizedName(std::string &norm_name, std::string_view name)
Return the normalized name.