Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RFieldSTLMisc.hxx
Go to the documentation of this file.
1/// \file ROOT/RField/STLMisc.hxx
2/// \author Jakob Blomer <jblomer@cern.ch>
3/// \date 2018-10-09
4
5/*************************************************************************
6 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
13#ifndef ROOT_RField_STLMisc
14#define ROOT_RField_STLMisc
15
16#ifndef ROOT_RField
17#error "Please include RField.hxx!"
18#endif
19
20#include <ROOT/RFieldBase.hxx>
21#include <ROOT/RNTupleTypes.hxx>
22
23#include <atomic>
24#include <bitset>
25#include <cstddef>
26#include <memory>
27#include <optional>
28#include <string>
29#include <string_view>
30#include <typeinfo>
31#include <variant>
32
33namespace ROOT {
34
35////////////////////////////////////////////////////////////////////////////////
36/// Template specializations for C++ std::atomic
37////////////////////////////////////////////////////////////////////////////////
38
39class RAtomicField : public RFieldBase {
40protected:
41 std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
42
43 void ConstructValue(void *where) const final { CallConstructValueOn(*fSubfields[0], where); }
44 std::unique_ptr<RDeleter> GetDeleter() const final { return GetDeleterOf(*fSubfields[0]); }
45
46 std::size_t AppendImpl(const void *from) final { return CallAppendOn(*fSubfields[0], from); }
49
50 void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;
51
52public:
53 RAtomicField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField);
56 ~RAtomicField() override = default;
57
58 std::vector<RValue> SplitValue(const RValue &value) const final;
59
60 size_t GetValueSize() const final { return fSubfields[0]->GetValueSize(); }
61 size_t GetAlignment() const final { return fSubfields[0]->GetAlignment(); }
62
64};
65
66template <typename ItemT>
67class RField<std::atomic<ItemT>> final : public RAtomicField {
68public:
69 static std::string TypeName() { return "std::atomic<" + RField<ItemT>::TypeName() + ">"; }
70 explicit RField(std::string_view name) : RAtomicField(name, std::make_unique<RField<ItemT>>("_0")) {}
71 RField(RField &&other) = default;
72 RField &operator=(RField &&other) = default;
74};
75
76////////////////////////////////////////////////////////////////////////////////
77/// Template specializations for C++ std::bitset
78////////////////////////////////////////////////////////////////////////////////
79
80/// The generic field for a `std::bitset<N>`.
81/// GCC and Clang store the bits in an array of unsigned long, whereas MSVC uses either a single uint32_t for `N <= 32`
82/// or an array of uint64_t for `N > 32` (reference: https://github.com/microsoft/STL/blob/main/stl/inc/bitset).
83/// TODO(jblomer): reading and writing efficiency should be improved; currently it is one bit at a time
84/// with an array of bools on the page level.
86protected:
87 std::size_t fN;
88
89protected:
90 std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
91 {
92 return std::make_unique<RBitsetField>(newName, fN);
93 }
94 const RColumnRepresentations &GetColumnRepresentations() const final;
98 std::size_t AppendImpl(const void *from) final;
99 void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
100 void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final;
101
102 size_t WordSize() const
103 {
104#if defined(_MSC_VER)
105 const size_t wordSize = (fN <= sizeof(unsigned long) * 8) ? sizeof(unsigned long) : sizeof(unsigned long long);
106#else
107 const size_t wordSize = sizeof(unsigned long);
108#endif
109 return wordSize;
110 }
111
112 template <typename FUlong, typename FUlonglong, typename... Args>
113 void SelectWordSize(FUlong &&fUlong, FUlonglong &&fUlonglong, Args &&...args);
114
115public:
116 RBitsetField(std::string_view fieldName, std::size_t N);
119 ~RBitsetField() override = default;
120
122 {
123 const size_t bitsPerWord = WordSize() * 8;
124 return WordSize() * ((fN + bitsPerWord - 1) / bitsPerWord);
125 }
126
128 {
129#if defined(_MSC_VER)
130 return WordSize() == sizeof(unsigned long) ? alignof(unsigned long) : alignof(unsigned long long);
131#else
132 return alignof(unsigned long);
133#endif
134 }
135
137
138 /// Get the number of bits in the bitset, i.e. the `N` in `std::bitset<N>`
139 std::size_t GetN() const { return fN; }
140};
141
142template <std::size_t N>
143class RField<std::bitset<N>> final : public RBitsetField {
144public:
145 static std::string TypeName() { return "std::bitset<" + std::to_string(N) + ">"; }
146 explicit RField(std::string_view name) : RBitsetField(name, N) {}
147 RField(RField &&other) = default;
148 RField &operator=(RField &&other) = default;
150};
151
152////////////////////////////////////////////////////////////////////////////////
153/// Template specializations for C++ std::byte
154////////////////////////////////////////////////////////////////////////////////
155
156extern template class RSimpleField<std::byte>;
157
158template <>
159class RField<std::byte> final : public RSimpleField<std::byte> {
160protected:
161 std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
162 {
163 return std::make_unique<RField>(newName);
164 }
165
166 const RColumnRepresentations &GetColumnRepresentations() const final;
167
168public:
169 static std::string TypeName() { return "std::byte"; }
170 explicit RField(std::string_view name) : RSimpleField(name, TypeName()) {}
171 RField(RField &&other) = default;
172 RField &operator=(RField &&other) = default;
174
175 void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
176};
177
178////////////////////////////////////////////////////////////////////////////////
179/// Template specializations for C++ std::optional and std::unique_ptr
180////////////////////////////////////////////////////////////////////////////////
181
182/// The field for values that may or may not be present in an entry. Parent class for unique pointer field and
183/// optional field. A nullable field cannot be instantiated itself but only its descendants.
184/// The RNullableField takes care of the on-disk representation. Child classes are responsible for the in-memory
185/// representation. Nullable fields use a `(Split)Index[64|32]` column to point to the available items.
187 /// The number of written non-null items in this cluster
189
190protected:
191 // For reading, indicates that we read type T as a nullable field of type T, i.e. the value is always present
192 bool fIsEvolvedFromInnerType = false;
193
195 void GenerateColumns() final;
197
198 std::size_t AppendNull();
199 std::size_t AppendValue(const void *from);
200 void CommitClusterImpl() final { fNWritten = 0; }
201
202 void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;
203
204 /// Given the global index of the nullable field, returns the corresponding cluster-local index of the subfield or,
205 /// if it is null, returns a default constructed RNTupleLocalIndex
207 /// Given the cluster-local index of the nullable field, returns the corresponding cluster-local index of
208 /// the subfield or, if it is null, returns a default constructed RNTupleLocalIndex
210
211 RNullableField(std::string_view fieldName, const std::string &typePrefix, std::unique_ptr<RFieldBase> itemField);
212
213public:
216 ~RNullableField() override = default;
217
219};
220
222 class ROptionalDeleter : public RDeleter {
223 private:
224 std::unique_ptr<RDeleter> fItemDeleter; // nullptr for trivially destructible items
225 std::size_t fEngagementPtrOffset = 0;
226
227 public:
228 ROptionalDeleter(std::unique_ptr<RDeleter> itemDeleter, std::size_t engagementPtrOffset, std::size_t alignment)
229 : RDeleter(alignment), fItemDeleter(std::move(itemDeleter)), fEngagementPtrOffset(engagementPtrOffset)
230 {
231 }
232 void operator()(void *objPtr, bool dtorOnly) final;
233 };
234
235 std::unique_ptr<RDeleter> fItemDeleter;
236
237 /// Given a pointer to an `std::optional<T>` in `optionalPtr`, extract a pointer to the engagement boolean.
238 /// Assumes that an `std::optional<T>` is stored as `struct { T t; bool engagement; };`
239 const bool *GetEngagementPtr(const void *optionalPtr) const;
240 bool *GetEngagementPtr(void *optionalPtr) const;
241 std::size_t GetEngagementPtrOffset() const;
242 void PrepareRead(void *to, bool hasOnDiskValue);
243
244protected:
245 std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
246
247 void ConstructValue(void *where) const final;
248 std::unique_ptr<RDeleter> GetDeleter() const final;
249
250 std::size_t AppendImpl(const void *from) final;
251 void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
252 void ReadInClusterImpl(ROOT::RNTupleLocalIndex localIndex, void *to) final;
253
254public:
255 ROptionalField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField);
259
260 std::vector<RValue> SplitValue(const RValue &value) const final;
261 std::size_t GetValueSize() const final;
262 std::size_t GetAlignment() const final;
263};
264
266class RField<std::optional<ItemT>> final : public ROptionalField {
267public:
268 static std::string TypeName() { return "std::optional<" + RField<ItemT>::TypeName() + ">"; }
269 explicit RField(std::string_view name) : ROptionalField(name, std::make_unique<RField<ItemT>>("_0")) {}
270 RField(RField &&other) = default;
271 RField &operator=(RField &&other) = default;
272 ~RField() final = default;
273};
274
277 private:
278 std::unique_ptr<RDeleter> fItemDeleter;
279
280 public:
281 explicit RUniquePtrDeleter(std::unique_ptr<RDeleter> itemDeleter)
282 : RDeleter(alignof(std::unique_ptr<char>)), fItemDeleter(std::move(itemDeleter))
283 {
284 }
285 void operator()(void *objPtr, bool dtorOnly) final;
286 };
287
288 std::unique_ptr<RDeleter> fItemDeleter;
289 /// If the item type is a polymorphic class (that declares or inherits at least one virtual method), points to the
290 /// expected dynamic type of any user object; otherwise nullptr.
291 const std::type_info *fPolymorphicTypeInfo = nullptr;
292
293 // Returns the value pointer, i.e. where to read the subfield into
294 void *PrepareRead(void *to, bool hasOnDiskValue);
295
296protected:
297 std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
298
299 void ConstructValue(void *where) const final { new (where) std::unique_ptr<char>(); }
300 std::unique_ptr<RDeleter> GetDeleter() const final;
301
302 std::size_t AppendImpl(const void *from) final;
303 void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
304 void ReadInClusterImpl(ROOT::RNTupleLocalIndex localIndex, void *to) final;
305
306public:
307 RUniquePtrField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField);
311
312 std::vector<RValue> SplitValue(const RValue &value) const final;
313 std::size_t GetValueSize() const final { return sizeof(std::unique_ptr<char>); }
314 std::size_t GetAlignment() const final { return alignof(std::unique_ptr<char>); }
315};
316
317template <typename ItemT>
318class RField<std::unique_ptr<ItemT>> final : public RUniquePtrField {
319public:
320 static std::string TypeName() { return "std::unique_ptr<" + RField<ItemT>::TypeName() + ">"; }
321 explicit RField(std::string_view name) : RUniquePtrField(name, std::make_unique<RField<ItemT>>("_0")) {}
322 RField(RField &&other) = default;
323 RField &operator=(RField &&other) = default;
324 ~RField() final = default;
325};
326
327////////////////////////////////////////////////////////////////////////////////
328/// Template specializations for C++ std::string
329////////////////////////////////////////////////////////////////////////////////
330
331template <>
332class RField<std::string> final : public RFieldBase {
333private:
335
336 std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final
337 {
338 return std::make_unique<RField>(newName);
339 }
340
341 const RColumnRepresentations &GetColumnRepresentations() const final;
342 void GenerateColumns() final;
343 void GenerateColumns(const ROOT::RNTupleDescriptor &desc) final;
344
345 void ConstructValue(void *where) const final { new (where) std::string(); }
346 std::unique_ptr<RDeleter> GetDeleter() const final { return std::make_unique<RTypedDeleter<std::string>>(); }
347
348 std::size_t AppendImpl(const void *from) final;
349 void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
350
351 void CommitClusterImpl() final { fIndex = 0; }
352
353public:
354 static std::string TypeName() { return "std::string"; }
355 explicit RField(std::string_view name)
356 : RFieldBase(name, TypeName(), ROOT::ENTupleStructure::kPlain, false /* isSimple */), fIndex(0)
357 {
358 }
359 RField(RField &&other) = default;
360 RField &operator=(RField &&other) = default;
361 ~RField() final = default;
362
363 std::size_t GetValueSize() const final { return sizeof(std::string); }
364 std::size_t GetAlignment() const final { return alignof(std::string); }
365 void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
366};
367
368////////////////////////////////////////////////////////////////////////////////
369/// Template specializations for C++ std::variant
370////////////////////////////////////////////////////////////////////////////////
371
372/// The generic field for std::variant types
373class RVariantField : public RFieldBase {
374private:
375 // Most compilers support at least 255 variants (256 - 1 value for the empty variant).
376 // Some compilers switch to a two-byte tag field already with 254 variants.
377 // MSVC only supports 163 variants in older versions, 250 in newer ones. It switches to a 2 byte
378 // tag as of 128 variants (at least in debug mode), so for simplicity we set the limit to 125 variants.
379 static constexpr std::size_t kMaxVariants = 125;
380
381 class RVariantDeleter : public RDeleter {
382 private:
383 std::size_t fTagOffset;
384 std::size_t fVariantOffset;
385 std::vector<std::unique_ptr<RDeleter>> fItemDeleters;
386
387 public:
388 RVariantDeleter(std::size_t tagOffset, std::size_t variantOffset, std::size_t alignment,
389 std::vector<std::unique_ptr<RDeleter>> itemDeleters)
391 fTagOffset(tagOffset),
392 fVariantOffset(variantOffset),
393 fItemDeleters(std::move(itemDeleters))
394 {
395 }
396 void operator()(void *objPtr, bool dtorOnly) final;
397 };
398
399 size_t fMaxItemSize = 0;
400 size_t fMaxAlignment = 1;
401 /// In the `std::variant` memory layout, at which byte number is the index stored
402 size_t fTagOffset = 0;
403 /// In the `std::variant` memory layout, the actual union of types may start at an offset > 0
404 size_t fVariantOffset = 0;
405 std::vector<ROOT::Internal::RColumnIndex::ValueType> fNWritten;
406
407 /// Extracts the index from an `std::variant` and transforms it into the 1-based index used for the switch column
408 /// The implementation supports two memory layouts that are in use: a trailing unsigned byte, zero-indexed,
409 /// having the exception caused empty state encoded by the max tag value,
410 /// or a trailing unsigned int instead of a char.
411 static std::uint8_t GetTag(const void *variantPtr, std::size_t tagOffset);
412 static void SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag);
413
414 RVariantField(std::string_view name, const RVariantField &source); // Used by CloneImpl()
415
416protected:
417 std::unique_ptr<RFieldBase> CloneImpl(std::string_view newName) const final;
418
419 const RColumnRepresentations &GetColumnRepresentations() const final;
420 void GenerateColumns() final;
421 void GenerateColumns(const ROOT::RNTupleDescriptor &desc) final;
422
423 void ConstructValue(void *where) const final;
424 std::unique_ptr<RDeleter> GetDeleter() const final;
425
426 std::size_t AppendImpl(const void *from) final;
427 void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;
428
429 void CommitClusterImpl() final;
430
431 void ReconcileOnDiskField(const RNTupleDescriptor &desc) final;
432
433public:
434 RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields);
438
439 std::size_t GetValueSize() const final;
440 std::size_t GetAlignment() const final;
441};
442
445private:
446 template <typename HeadT, typename... TailTs>
447 static std::string BuildItemTypes()
448 {
449 std::string result = RField<HeadT>::TypeName();
450 if constexpr (sizeof...(TailTs) > 0)
451 result += "," + BuildItemTypes<TailTs...>();
452 return result;
453 }
454
455 template <typename HeadT, typename... TailTs>
456 static void _BuildItemFields(std::vector<std::unique_ptr<RFieldBase>> &itemFields, unsigned int index = 0)
457 {
458 itemFields.emplace_back(new RField<HeadT>("_" + std::to_string(index)));
459 if constexpr (sizeof...(TailTs) > 0)
460 _BuildItemFields<TailTs...>(itemFields, index + 1);
461 }
462 static std::vector<std::unique_ptr<RFieldBase>> BuildItemFields()
463 {
464 std::vector<std::unique_ptr<RFieldBase>> result;
465 _BuildItemFields<ItemTs...>(result);
466 return result;
467 }
468
469public:
470 static std::string TypeName() { return "std::variant<" + BuildItemTypes<ItemTs...>() + ">"; }
471 explicit RField(std::string_view name) : RVariantField(name, BuildItemFields()) {}
472 RField(RField &&other) = default;
473 RField &operator=(RField &&other) = default;
474 ~RField() final = default;
475};
476
477} // namespace ROOT
478
479#endif
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define N
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 value
char name[80]
Definition TGX11.cxx:148
TCanvas * alignment()
Definition alignment.C:1
Abstract base class for classes implementing the visitor design pattern.
The in-memory representation of a 32bit or 64bit on-disk index column.
Template specializations for C++ std::atomic.
RAtomicField(RAtomicField &&other)=default
size_t GetValueSize() const final
What sizeof(T) for this type returns.
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 GetAlignment() const final
What alignof(T) for this type returns.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:1263
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
RAtomicField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:1225
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:1240
void ReadInClusterImpl(RNTupleLocalIndex localIndex, 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) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
Definition RField.cxx:1246
~RAtomicField() override=default
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
Definition RField.cxx:1256
RAtomicField & operator=(RAtomicField &&other)=default
Template specializations for C++ std::bitset.
std::size_t GetN() const
Get the number of bits in the bitset, i.e. the N in std::bitset<N>
size_t GetAlignment() const final
What alignof(T) for this type returns.
size_t WordSize() const
~RBitsetField() override=default
RBitsetField & operator=(RBitsetField &&other)=default
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
What sizeof(T) for this type returns.
RBitsetField(RBitsetField &&other)=default
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.
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
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
void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final
The list of column representations a field can have.
A functor to release the memory acquired by CreateValue() (memory and constructor).
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.
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
virtual const RColumnRepresentations & GetColumnRepresentations() const
Implementations in derived classes should return a static RColumnRepresentations object.
virtual void GenerateColumns()
Implementations in derived classes should create the backing columns corresponding to the field type ...
static std::unique_ptr< RDeleter > GetDeleterOf(const RFieldBase &other)
static std::size_t CallAppendOn(RFieldBase &other, const void *from)
Allow derived classes to call Append() and Read() on other (sub)fields.
virtual void CommitClusterImpl()
static void CallReadOn(RFieldBase &other, RNTupleLocalIndex localIndex, void *to)
static void CallConstructValueOn(const RFieldBase &other, void *where)
Allow derived classes to call ConstructValue(void *) and GetDeleter() on other (sub)fields.
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:319
~RField() final=default
RField & operator=(RField &&other)=default
static std::string TypeName()
Definition RField.hxx:321
RField(std::string_view name)
Definition RField.hxx:322
The on-storage metadata of an RNTuple.
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
Template specializations for C++ std::optional and std::unique_ptr.
~RNullableField() override=default
RNullableField & operator=(RNullableField &&other)=default
RNullableField(RNullableField &&other)=default
ROptionalDeleter(std::unique_ptr< RDeleter > itemDeleter, std::size_t engagementPtrOffset, std::size_t alignment)
std::unique_ptr< RDeleter > fItemDeleter
std::size_t GetEngagementPtrOffset() const
std::unique_ptr< RDeleter > fItemDeleter
std::unique_ptr< RDeleter > fItemDeleter
RUniquePtrDeleter(std::unique_ptr< RDeleter > itemDeleter)
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
std::unique_ptr< RDeleter > fItemDeleter
std::size_t GetAlignment() const final
What alignof(T) for this type returns.
std::vector< std::unique_ptr< RDeleter > > fItemDeleters
RVariantDeleter(std::size_t tagOffset, std::size_t variantOffset, std::size_t alignment, std::vector< std::unique_ptr< RDeleter > > itemDeleters)
Template specializations for C++ std::variant.
std::vector< ROOT::Internal::RColumnIndex::ValueType > fNWritten
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.