Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleModel.hxx
Go to the documentation of this file.
1/// \file ROOT/RNTupleModel.hxx
2/// \ingroup NTuple ROOT7
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2018-10-04
5/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6/// is welcome!
7
8/*************************************************************************
9 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
10 * All rights reserved. *
11 * *
12 * For the licensing terms see $ROOTSYS/LICENSE. *
13 * For the list of contributors see $ROOTSYS/README/CREDITS. *
14 *************************************************************************/
15
16#ifndef ROOT7_RNTupleModel
17#define ROOT7_RNTupleModel
18
19#include <ROOT/REntry.hxx>
20#include <ROOT/RError.hxx>
21#include <ROOT/RField.hxx>
22#include <ROOT/RNTupleUtil.hxx>
23#include <string_view>
24
25#include <cstdint>
26#include <functional>
27#include <memory>
28#include <string>
29#include <unordered_map>
30#include <unordered_set>
31#include <utility>
32
33namespace ROOT {
34namespace Experimental {
35
36class RNTupleModel;
37class RNTupleWriter;
38class RNTupleWriteOptions;
39
40namespace Internal {
41class RProjectedFields;
42
43RFieldZero &GetFieldZeroOfModel(RNTupleModel &model);
44RProjectedFields &GetProjectedFieldsOfModel(RNTupleModel &model);
45
46// clang-format off
47/**
48\class ROOT::Experimental::Internal::RProjectedFields
49\ingroup NTuple
50\brief The projected fields of a `RNTupleModel`
51
52Projected fields are fields whose columns are reused from existing fields. Projected fields are not attached
53to the models zero field. Only the real source fields are written to, projected fields are stored as meta-data
54(header) information only. Only top-level projected fields are supported because otherwise the layout of types
55could be altered in unexpected ways.
56All projected fields and the source fields used to back them are kept in this class.
57*/
58// clang-format on
60public:
61 /// The map keys are the projected target fields, the map values are the backing source fields
62 /// Note that sub fields are treated individually and indepently of their parent field
63 using FieldMap_t = std::unordered_map<const RFieldBase *, const RFieldBase *>;
64
65private:
66 explicit RProjectedFields(std::unique_ptr<RFieldZero> fieldZero) : fFieldZero(std::move(fieldZero)) {}
67 /// The projected fields are attached to this zero field
68 std::unique_ptr<RFieldZero> fFieldZero;
69 /// Maps the source fields from fModel to the target projected fields attached to fFieldZero
71 /// The model this set of projected fields belongs to
73
74 /// Asserts that the passed field is a valid target of the source field provided in the field map.
75 /// Checks the field without looking into sub fields.
77
78public:
79 explicit RProjectedFields(const RNTupleModel &model) : fFieldZero(std::make_unique<RFieldZero>()), fModel(&model) {}
84 ~RProjectedFields() = default;
85
86 /// The new model needs to be a clone of fModel
87 std::unique_ptr<RProjectedFields> Clone(const RNTupleModel &newModel) const;
88
90 const RFieldBase *GetSourceField(const RFieldBase *target) const;
91 /// Adds a new projected field. The field map needs to provide valid source fields of fModel for 'field'
92 /// and each of its sub fields.
93 RResult<void> Add(std::unique_ptr<RFieldBase> field, const FieldMap_t &fieldMap);
94 bool IsEmpty() const { return fFieldZero->begin() == fFieldZero->end(); }
95};
96
97// clang-format off
98/**
99\class ROOT::Experimental::Internal::RNTupleModelChangeset
100\ingroup NTuple
101\brief The incremental changes to a `RNTupleModel`
102
103Represents a set of alterations to a `RNTupleModel` that happened after the model is used to initialize a `RPageSink`
104instance. This object can be used to communicate metadata updates to a `RPageSink`.
105You will not normally use this directly; see `RNTupleModel::RUpdater` instead.
106*/
107// clang-format on
110 /// Points to the fields in fModel that were added as part of an updater transaction
111 std::vector<RFieldBase *> fAddedFields;
112 /// Points to the projected fields in fModel that were added as part of an updater transaction
113 std::vector<RFieldBase *> fAddedProjectedFields;
114
116 bool IsEmpty() const { return fAddedFields.empty() && fAddedProjectedFields.empty(); }
117};
118
119} // namespace Internal
120
121// clang-format off
122/**
123\class ROOT::Experimental::RNTupleModel
124\ingroup NTuple
125\brief The RNTupleModel encapulates the schema of an ntuple.
126
127The ntuple model comprises a collection of hierarchically organized fields. From a model, "entries"
128can be extracted. For convenience, the model provides a default entry unless it is created as a "bare model".
129Models have a unique model identifier that faciliates checking whether entries are compatible with it
130(i.e.: have been extracted from that model).
131
132A model is subject to a state transition during its lifetime: it starts in a building state, in which fields can be
133added and modified. Once the schema is finalized, the model gets frozen. Only frozen models can create entries.
134*/
135// clang-format on
139
140public:
141 /// User provided function that describes the mapping of existing source fields to projected fields in terms
142 /// of fully qualified field names. The mapping function is called with the qualified field names of the provided
143 /// field and the subfields. It should return the qualified field names used as a mapping source.
144 using FieldMappingFunc_t = std::function<std::string(const std::string &)>;
145
146 /// A wrapper over a field name and an optional description; used in `AddField()` and `RUpdater::AddField()`
149 NameWithDescription_t(const std::string &name) : fName(name) {}
150 NameWithDescription_t(std::string_view name) : fName(name) {}
151 NameWithDescription_t(std::string_view name, std::string_view descr) : fName(name), fDescription(descr) {}
152
153 std::string_view fName;
154 std::string_view fDescription = "";
155 };
156
157 /// A model is usually immutable after passing it to an `RNTupleWriter`. However, for the rare
158 /// cases that require changing the model after the fact, `RUpdater` provides limited support for
159 /// incremental updates, e.g. addition of new fields.
160 ///
161 /// See `RNTupleWriter::CreateModelUpdater()` for an example.
162 class RUpdater {
163 private:
166 std::uint64_t fNewModelId = 0; ///< The model ID after committing
167
168 public:
169 explicit RUpdater(RNTupleWriter &writer);
171 /// Begin a new set of alterations to the underlying model. As a side effect, all `REntry` instances related to
172 /// the model are invalidated.
173 void BeginUpdate();
174 /// Commit changes since the last call to `BeginUpdate()`. All the invalidated `REntry`s remain invalid.
175 /// `CreateEntry()` or `CreateBareEntry()` can be used to create an `REntry` that matching the new model.
176 /// Upon completion, `BeginUpdate()` can be called again to begin a new set of changes.
177 void CommitUpdate();
178
179 template <typename T, typename... ArgsT>
180 std::shared_ptr<T> MakeField(const NameWithDescription_t &fieldNameDesc, ArgsT &&...args)
181 {
182 auto objPtr = fOpenChangeset.fModel.MakeField<T>(fieldNameDesc, std::forward<ArgsT>(args)...);
183 auto fieldZero = fOpenChangeset.fModel.fFieldZero.get();
184 auto it = std::find_if(fieldZero->begin(), fieldZero->end(),
185 [&](const auto &f) { return f.GetFieldName() == fieldNameDesc.fName; });
186 R__ASSERT(it != fieldZero->end());
187 fOpenChangeset.fAddedFields.emplace_back(&(*it));
188 return objPtr;
189 }
190
191 void AddField(std::unique_ptr<RFieldBase> field);
192
193 RResult<void> AddProjectedField(std::unique_ptr<RFieldBase> field, FieldMappingFunc_t mapping);
194 };
195
196private:
197 /// Hierarchy of fields consisting of simple types and collections (sub trees)
198 std::unique_ptr<RFieldZero> fFieldZero;
199 /// Contains field values corresponding to the created top-level fields, as well as registered subfields
200 std::unique_ptr<REntry> fDefaultEntry;
201 /// Keeps track of which field names are taken, including projected field names.
202 std::unordered_set<std::string> fFieldNames;
203 /// Free text set by the user
204 std::string fDescription;
205 /// The set of projected top-level fields
206 std::unique_ptr<Internal::RProjectedFields> fProjectedFields;
207 /// Keeps track of which subfields have been registered to be included in entries belonging to this model.
208 std::unordered_set<std::string> fRegisteredSubfields;
209 /// Every model has a unique ID to distinguish it from other models. Entries are linked to models via the ID.
210 /// Cloned models get a new model ID.
211 std::uint64_t fModelId = 0;
212 /// Models have a separate schema ID to remember that the clone of a frozen model still has the same schema.
213 std::uint64_t fSchemaId = 0;
214 /// Changed by Freeze() / Unfreeze() and by the RUpdater.
215 bool fIsFrozen = false;
216
217 /// Checks that user-provided field names are valid in the context of this RNTuple model.
218 /// Throws an RException for invalid names, empty names (which is reserved for the zero field) and duplicate field
219 /// names.
220 void EnsureValidFieldName(std::string_view fieldName);
221
222 /// Throws an RException if fFrozen is true
223 void EnsureNotFrozen() const;
224
225 /// Throws an RException if fDefaultEntry is nullptr
226 void EnsureNotBare() const;
227
228 /// The field name can be a top-level field or a nested field. Returns nullptr if the field is not in the model.
229 RFieldBase *FindField(std::string_view fieldName) const;
230
231 /// Add a subfield to the provided entry. If `initializeValue` is false, a nullptr will be bound to the entry value
232 /// (used in bare models).
233 void AddSubfield(std::string_view fieldName, REntry &entry, bool initializeValue = true) const;
234
235 RNTupleModel(std::unique_ptr<RFieldZero> fieldZero);
236
237public:
238 RNTupleModel(const RNTupleModel&) = delete;
240 ~RNTupleModel() = default;
241
242 std::unique_ptr<RNTupleModel> Clone() const;
243 static std::unique_ptr<RNTupleModel> Create();
244 static std::unique_ptr<RNTupleModel> Create(std::unique_ptr<RFieldZero> fieldZero);
245 /// A bare model has no default entry
246 static std::unique_ptr<RNTupleModel> CreateBare();
247 static std::unique_ptr<RNTupleModel> CreateBare(std::unique_ptr<RFieldZero> fieldZero);
248
249 /// Creates a new field given a `name` or `{name, description}` pair and a
250 /// corresponding value that is managed by a shared pointer.
251 ///
252 /// **Example: create some fields and fill an %RNTuple**
253 /// ~~~ {.cpp}
254 /// #include <ROOT/RNTupleModel.hxx>
255 /// #include <ROOT/RNTupleWriter.hxx>
256 /// using ROOT::Experimental::RNTupleModel;
257 /// using ROOT::Experimental::RNTupleWriter;
258 ///
259 /// #include <vector>
260 ///
261 /// auto model = RNTupleModel::Create();
262 /// auto pt = model->MakeField<float>("pt");
263 /// auto vec = model->MakeField<std::vector<int>>("vec");
264 ///
265 /// // The RNTuple is written to disk when the RNTupleWriter goes out of scope
266 /// {
267 /// auto writer = RNTupleWriter::Recreate(std::move(model), "myNTuple", "myFile.root");
268 /// for (int i = 0; i < 100; i++) {
269 /// *pt = static_cast<float>(i);
270 /// *vec = {i, i+1, i+2};
271 /// writer->Fill();
272 /// }
273 /// }
274 /// ~~~
275 ///
276 /// **Example: create a field with an initial value**
277 /// ~~~ {.cpp}
278 /// #include <ROOT/RNTupleModel.hxx>
279 /// using ROOT::Experimental::RNTupleModel;
280 ///
281 /// auto model = RNTupleModel::Create();
282 /// // pt's initial value is 42.0
283 /// auto pt = model->MakeField<float>("pt", 42.0);
284 /// ~~~
285 /// **Example: create a field with a description**
286 /// ~~~ {.cpp}
287 /// #include <ROOT/RNTupleModel.hxx>
288 /// using ROOT::Experimental::RNTupleModel;
289 ///
290 /// auto model = RNTupleModel::Create();
291 /// auto hadronFlavour = model->MakeField<float>({
292 /// "hadronFlavour", "flavour from hadron ghost clustering"
293 /// });
294 /// ~~~
295 template <typename T, typename... ArgsT>
296 std::shared_ptr<T> MakeField(const NameWithDescription_t &fieldNameDesc, ArgsT &&...args)
297 {
299 EnsureValidFieldName(fieldNameDesc.fName);
300 auto field = std::make_unique<RField<T>>(fieldNameDesc.fName);
301 field->SetDescription(fieldNameDesc.fDescription);
302 std::shared_ptr<T> ptr;
303 if (fDefaultEntry)
304 ptr = fDefaultEntry->AddValue<T>(*field, std::forward<ArgsT>(args)...);
305 fFieldNames.insert(field->GetFieldName());
306 fFieldZero->Attach(std::move(field));
307 return ptr;
308 }
309
310 /// Adds a field whose type is not known at compile time. Thus there is no shared pointer returned.
311 ///
312 /// Throws an exception if the field is null.
313 void AddField(std::unique_ptr<RFieldBase> field);
314
315 /// Register a subfield so it can be accessed directly from entries belonging to the model. Because registering a
316 /// subfield does not fundamentally change the model, previously created entries will not be invalidated, nor
317 /// modified in any way; a registered subfield is merely an accessor added to the default entry (if present) and any
318 /// entries created afterwards.
319 ///
320 /// Using models with registered subfields for writing is not allowed. Attempting to do so will result in an
321 /// exception.
322 ///
323 /// Throws an exception if the provided subfield could not be found in the model.
324 void RegisterSubfield(std::string_view qualifiedFieldName);
325
326 /// Adds a top-level field based on existing fields.
327 ///
328 /// The mapping function takes one argument, which is a string containing the name of the projected field. The return
329 /// value of the mapping function should be the name of the (existing) field onto which the projection is made.
330 /// **Example**
331 /// ~~~ {.cpp}
332 /// auto model = RNTupleModel::Create();
333 /// model->MakeField<float>("met");
334 /// auto metProjection = RFieldBase::Create("missingE", "float").Unwrap();
335 /// model->AddProjectedField(std::move(metProjection), [](const std::string &) { return "met"; });
336 /// ~~~
337 ///
338 /// Adding projections for collection fields is also possible, as long as they follow the same schema structure. For
339 /// example, a projection of a collection of structs onto a collection of scalars is possible, but a projection of a
340 /// collection of a collection of scalars onto a collection of scalars is not.
341 ///
342 /// In the case of projections for nested fields, the mapping function must provide a mapping for every nesting
343 /// level.
344 /// **Example**
345 /// ~~~ {.cpp}
346 /// struct P { int x, y; };
347 ///
348 /// auto model = RNTupleModel::Create();
349 /// model->MakeField<std::vector<P>>("points");
350 /// auto pxProjection = RFieldBase::Create("pxs", "std::vector<int>").Unwrap();
351 /// model->AddProjectedField(std::move(pxProjection), [](const std::string &fieldName) {
352 /// if (fieldName == "pxs")
353 /// return "points";
354 /// else
355 /// return "points._0.x";
356 /// });
357 /// ~~~
358 ///
359 /// Creating projections for fields containing `std::variant` or fixed-size arrays is unsupported.
360 RResult<void> AddProjectedField(std::unique_ptr<RFieldBase> field, FieldMappingFunc_t mapping);
361
362 void Freeze();
363 void Unfreeze();
364 bool IsFrozen() const { return fIsFrozen; }
365 bool IsBare() const { return !fDefaultEntry; }
366 std::uint64_t GetModelId() const { return fModelId; }
367 std::uint64_t GetSchemaId() const { return fSchemaId; }
368
369 std::unique_ptr<REntry> CreateEntry() const;
370 /// In a bare entry, all values point to nullptr. The resulting entry shall use BindValue() in order
371 /// set memory addresses to be serialized / deserialized
372 std::unique_ptr<REntry> CreateBareEntry() const;
373 /// Creates a token to be used in REntry methods to address a field present in the entry
374 REntry::RFieldToken GetToken(std::string_view fieldName) const;
375 /// Calls the given field's CreateBulk() method. Throws an exception if no field with the given name exists.
376 RFieldBase::RBulk CreateBulk(std::string_view fieldName) const;
377
379 const REntry &GetDefaultEntry() const;
380
381 /// Mutable access to the root field is used to make adjustments to the fields.
383 const RFieldZero &GetConstFieldZero() const { return *fFieldZero; }
384 RFieldBase &GetMutableField(std::string_view fieldName);
385 const RFieldBase &GetConstField(std::string_view fieldName) const;
386
387 const std::string &GetDescription() const { return fDescription; }
388 void SetDescription(std::string_view description);
389
390 /// Get the (qualified) names of subfields that have been registered to be included in entries from this model.
391 const std::unordered_set<std::string> &GetRegisteredSubfields() const { return fRegisteredSubfields; }
392
393 /// Estimate the memory usage for this model during writing
394 ///
395 /// This will return an estimate in bytes for the internal page and compression buffers. The value should be
396 /// understood per sequential RNTupleWriter or per RNTupleFillContext created for a RNTupleParallelWriter
397 /// constructed with this model.
398 std::size_t EstimateWriteMemoryUsage(const RNTupleWriteOptions &options = RNTupleWriteOptions()) const;
399};
400
401} // namespace Experimental
402} // namespace ROOT
403
404#endif
#define f(i)
Definition RSha256.hxx:104
#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
char name[80]
Definition TGX11.cxx:110
The projected fields of a RNTupleModel
FieldMap_t fFieldMap
Maps the source fields from fModel to the target projected fields attached to fFieldZero.
const RNTupleModel * fModel
The model this set of projected fields belongs to.
std::unique_ptr< RProjectedFields > Clone(const RNTupleModel &newModel) const
The new model needs to be a clone of fModel.
RResult< void > EnsureValidMapping(const RFieldBase *target, const FieldMap_t &fieldMap)
Asserts that the passed field is a valid target of the source field provided in the field map.
RProjectedFields(const RProjectedFields &)=delete
RProjectedFields & operator=(RProjectedFields &&)=default
std::unordered_map< const RFieldBase *, const RFieldBase * > FieldMap_t
The map keys are the projected target fields, the map values are the backing source fields Note that ...
const RFieldBase * GetSourceField(const RFieldBase *target) const
RProjectedFields(std::unique_ptr< RFieldZero > fieldZero)
RProjectedFields(RProjectedFields &&)=default
RResult< void > Add(std::unique_ptr< RFieldBase > field, const FieldMap_t &fieldMap)
Adds a new projected field.
RProjectedFields & operator=(const RProjectedFields &)=delete
std::unique_ptr< RFieldZero > fFieldZero
The projected fields are attached to this zero field.
The field token identifies a (sub)field in this entry.
Definition REntry.hxx:61
The REntry is a collection of values in an ntuple corresponding to a complete row in the data set.
Definition REntry.hxx:51
Similar to RValue but manages an array of consecutive values.
A field translates read and write calls from/to underlying columns to/from tree values.
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:58
A model is usually immutable after passing it to an RNTupleWriter.
Internal::RNTupleModelChangeset fOpenChangeset
void CommitUpdate()
Commit changes since the last call to BeginUpdate().
void BeginUpdate()
Begin a new set of alterations to the underlying model.
std::uint64_t fNewModelId
The model ID after committing.
std::shared_ptr< T > MakeField(const NameWithDescription_t &fieldNameDesc, ArgsT &&...args)
RResult< void > AddProjectedField(std::unique_ptr< RFieldBase > field, FieldMappingFunc_t mapping)
void AddField(std::unique_ptr< RFieldBase > field)
The RNTupleModel encapulates the schema of an ntuple.
std::unordered_set< std::string > fRegisteredSubfields
Keeps track of which subfields have been registered to be included in entries belonging to this model...
std::unordered_set< std::string > fFieldNames
Keeps track of which field names are taken, including projected field names.
std::string fDescription
Free text set by the user.
void EnsureValidFieldName(std::string_view fieldName)
Checks that user-provided field names are valid in the context of this RNTuple model.
std::uint64_t fModelId
Every model has a unique ID to distinguish it from other models.
std::function< std::string(const std::string &)> FieldMappingFunc_t
User provided function that describes the mapping of existing source fields to projected fields in te...
std::unique_ptr< Internal::RProjectedFields > fProjectedFields
The set of projected top-level fields.
std::uint64_t GetModelId() const
const RFieldZero & GetConstFieldZero() const
const RFieldBase & GetConstField(std::string_view fieldName) const
RNTupleModel(const RNTupleModel &)=delete
std::uint64_t fSchemaId
Models have a separate schema ID to remember that the clone of a frozen model still has the same sche...
REntry::RFieldToken GetToken(std::string_view fieldName) const
Creates a token to be used in REntry methods to address a field present in the entry.
void EnsureNotBare() const
Throws an RException if fDefaultEntry is nullptr.
std::unique_ptr< RNTupleModel > Clone() const
void EnsureNotFrozen() const
Throws an RException if fFrozen is true.
RFieldZero & GetMutableFieldZero()
Mutable access to the root field is used to make adjustments to the fields.
std::size_t EstimateWriteMemoryUsage(const RNTupleWriteOptions &options=RNTupleWriteOptions()) const
Estimate the memory usage for this model during writing.
std::shared_ptr< T > MakeField(const NameWithDescription_t &fieldNameDesc, ArgsT &&...args)
Creates a new field given a name or {name, description} pair and a corresponding value that is manage...
const std::unordered_set< std::string > & GetRegisteredSubfields() const
Get the (qualified) names of subfields that have been registered to be included in entries from this ...
std::unique_ptr< REntry > CreateBareEntry() const
In a bare entry, all values point to nullptr.
std::unique_ptr< REntry > CreateEntry() const
RFieldBase::RBulk CreateBulk(std::string_view fieldName) const
Calls the given field's CreateBulk() method. Throws an exception if no field with the given name exis...
static std::unique_ptr< RNTupleModel > Create()
std::uint64_t GetSchemaId() const
void AddSubfield(std::string_view fieldName, REntry &entry, bool initializeValue=true) const
Add a subfield to the provided entry.
RResult< void > AddProjectedField(std::unique_ptr< RFieldBase > field, FieldMappingFunc_t mapping)
Adds a top-level field based on existing fields.
void SetDescription(std::string_view description)
std::unique_ptr< REntry > fDefaultEntry
Contains field values corresponding to the created top-level fields, as well as registered subfields.
RFieldBase * FindField(std::string_view fieldName) const
The field name can be a top-level field or a nested field. Returns nullptr if the field is not in the...
RFieldBase & GetMutableField(std::string_view fieldName)
static std::unique_ptr< RNTupleModel > CreateBare()
A bare model has no default entry.
const std::string & GetDescription() const
void AddField(std::unique_ptr< RFieldBase > field)
Adds a field whose type is not known at compile time.
void RegisterSubfield(std::string_view qualifiedFieldName)
Register a subfield so it can be accessed directly from entries belonging to the model.
RNTupleModel & operator=(const RNTupleModel &)=delete
bool fIsFrozen
Changed by Freeze() / Unfreeze() and by the RUpdater.
std::unique_ptr< RFieldZero > fFieldZero
Hierarchy of fields consisting of simple types and collections (sub trees)
Common user-tunable settings for storing ntuples.
An RNTuple that gets filled with entries (data) and writes them to storage.
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:194
RProjectedFields & GetProjectedFieldsOfModel(RNTupleModel &model)
RFieldZero & GetFieldZeroOfModel(RNTupleModel &model)
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
The incremental changes to a RNTupleModel
std::vector< RFieldBase * > fAddedProjectedFields
Points to the projected fields in fModel that were added as part of an updater transaction.
std::vector< RFieldBase * > fAddedFields
Points to the fields in fModel that were added as part of an updater transaction.
A wrapper over a field name and an optional description; used in AddField() and RUpdater::AddField()
NameWithDescription_t(std::string_view name, std::string_view descr)