Logo ROOT  
Reference Guide
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 <ROOT/RFieldValue.hxx>
24#include <ROOT/RStringView.hxx>
25
26#include <cstdint>
27#include <functional>
28#include <memory>
29#include <string>
30#include <unordered_map>
31#include <unordered_set>
32#include <utility>
33
34namespace ROOT {
35namespace Experimental {
36
37class RCollectionNTupleWriter;
38
39// clang-format off
40/**
41\class ROOT::Experimental::RNTupleModel
42\ingroup NTuple
43\brief The RNTupleModel encapulates the schema of an ntuple.
44
45The ntuple model comprises a collection of hierarchically organized fields. From a model, "entries"
46can be extracted. For convenience, the model provides a default entry. Models have a unique model identifier
47that faciliates checking whether entries are compatible with it (i.e.: have been extracted from that model).
48*/
49// clang-format on
51public:
52 /// Projected fields are fields whose columns are reused from existing fields. Projected fields are not attached
53 /// to 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
55 /// could be altered in unexpected ways.
56 /// All projected fields and the source fields used to back them are kept in this class.
58 public:
59 /// The map keys are the projected target fields, the map values are the backing source fields
60 /// Note that sub fields are treated individually and indepently of their parent field
61 using FieldMap_t = std::unordered_map<const Detail::RFieldBase *, const Detail::RFieldBase *>;
62
63 private:
64 explicit RProjectedFields(std::unique_ptr<RFieldZero> fieldZero) : fFieldZero(std::move(fieldZero)) {}
65 /// The projected fields are attached to this zero field
66 std::unique_ptr<RFieldZero> fFieldZero;
67 /// Maps the source fields from fModel to the target projected fields attached to fFieldZero
69 /// The model this set of projected fields belongs to
71
72 /// Asserts that the passed field is a valid target of the source field provided in the field map.
73 /// Checks the field without looking into sub fields.
75
76 public:
77 explicit RProjectedFields(const RNTupleModel *model) : fFieldZero(std::make_unique<RFieldZero>()), fModel(model)
78 {
79 }
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
89 RFieldZero *GetFieldZero() const { return fFieldZero.get(); }
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<Detail::RFieldBase> field, const FieldMap_t &fieldMap);
94 bool IsEmpty() const { return fFieldZero->begin() == fFieldZero->end(); }
95 };
96
97private:
98 /// Hierarchy of fields consisting of simple types and collections (sub trees)
99 std::unique_ptr<RFieldZero> fFieldZero;
100 /// Contains field values corresponding to the created top-level fields
101 std::unique_ptr<REntry> fDefaultEntry;
102 /// Keeps track of which field names are taken, including projected field names.
103 std::unordered_set<std::string> fFieldNames;
104 /// Free text set by the user
105 std::string fDescription;
106 /// The set of projected top-level fields
107 std::unique_ptr<RProjectedFields> fProjectedFields;
108 /// Upon freezing, every model has a unique ID to distingusish it from other models. Cloning preserves the ID.
109 /// Entries are linked to models via the ID.
110 std::uint64_t fModelId = 0;
111
112 /// Checks that user-provided field names are valid in the context
113 /// of this NTuple model. Throws an RException for invalid names.
115
116 /// Throws an RException if fFrozen is true
117 void EnsureNotFrozen() const;
118
119 /// Throws an RException if fDefaultEntry is nullptr
120 void EnsureNotBare() const;
121
122 RNTupleModel();
123
124public:
125 RNTupleModel(const RNTupleModel&) = delete;
127 ~RNTupleModel() = default;
128
129 std::unique_ptr<RNTupleModel> Clone() const;
130 static std::unique_ptr<RNTupleModel> Create();
131 /// A bare model has no default entry
132 static std::unique_ptr<RNTupleModel> CreateBare();
133
134 /// Creates a new field and a corresponding tree value that is managed by a shared pointer.
135 ///
136 /// **Example: create some fields and fill an %RNTuple**
137 /// ~~~ {.cpp}
138 /// #include <ROOT/RNTuple.hxx>
139 /// using ROOT::Experimental::RNTupleModel;
140 /// using ROOT::Experimental::RNTupleWriter;
141 ///
142 /// #include <vector>
143 ///
144 /// auto model = RNTupleModel::Create();
145 /// auto pt = model->MakeField<float>("pt");
146 /// auto vec = model->MakeField<std::vector<int>>("vec");
147 ///
148 /// // The RNTuple is written to disk when the RNTupleWriter goes out of scope
149 /// {
150 /// auto ntuple = RNTupleWriter::Recreate(std::move(model), "myNTuple", "myFile.root");
151 /// for (int i = 0; i < 100; i++) {
152 /// *pt = static_cast<float>(i);
153 /// *vec = {i, i+1, i+2};
154 /// ntuple->Fill();
155 /// }
156 /// }
157 /// ~~~
158 /// **Example: create a field with an initial value**
159 /// ~~~ {.cpp}
160 /// #include <ROOT/RNTuple.hxx>
161 /// using ROOT::Experimental::RNTupleModel;
162 ///
163 /// auto model = RNTupleModel::Create();
164 /// // pt's initial value is 42.0
165 /// auto pt = model->MakeField<float>("pt", 42.0);
166 /// ~~~
167 template <typename T, typename... ArgsT>
168 std::shared_ptr<T> MakeField(std::string_view fieldName, ArgsT&&... args) {
169 return MakeField<T>({fieldName, ""}, std::forward<ArgsT>(args)...);
170 }
171
172 /// Creates a new field given a `{name, description}` pair and a corresponding tree value that
173 /// is managed by a shared pointer.
174 ///
175 /// **Example: create a field with a description**
176 /// ~~~ {.cpp}
177 /// #include <ROOT/RNTuple.hxx>
178 /// using ROOT::Experimental::RNTupleModel;
179 ///
180 /// auto model = RNTupleModel::Create();
181 /// auto hadronFlavour = model->MakeField<float>({
182 /// "hadronFlavour", "flavour from hadron ghost clustering"
183 /// });
184 /// ~~~
185 template <typename T, typename... ArgsT>
186 std::shared_ptr<T> MakeField(std::pair<std::string_view, std::string_view> fieldNameDesc,
187 ArgsT&&... args)
188 {
190 EnsureValidFieldName(fieldNameDesc.first);
191 auto field = std::make_unique<RField<T>>(fieldNameDesc.first);
192 field->SetDescription(fieldNameDesc.second);
193 std::shared_ptr<T> ptr;
194 if (fDefaultEntry)
195 ptr = fDefaultEntry->AddValue<T>(field.get(), std::forward<ArgsT>(args)...);
196 fFieldZero->Attach(std::move(field));
197 return ptr;
198 }
199
200 /// Adds a field whose type is not known at compile time. Thus there is no shared pointer returned.
201 ///
202 /// Throws an exception if the field is null.
203 void AddField(std::unique_ptr<Detail::RFieldBase> field);
204
205 /// Throws an exception if fromWhere is null.
206 template <typename T>
207 void AddField(std::string_view fieldName, T* fromWhere) {
208 AddField<T>({fieldName, ""}, fromWhere);
209 }
210
211 /// Throws an exception if fromWhere is null.
212 template <typename T>
213 void AddField(std::pair<std::string_view, std::string_view> fieldNameDesc, T* fromWhere) {
216 if (!fromWhere)
217 throw RException(R__FAIL("null field fromWhere"));
218 EnsureValidFieldName(fieldNameDesc.first);
219
220 auto field = std::make_unique<RField<T>>(fieldNameDesc.first);
221 field->SetDescription(fieldNameDesc.second);
222 fDefaultEntry->CaptureValue(field->CaptureValue(fromWhere));
223 fFieldZero->Attach(std::move(field));
224 }
225
226 /// Adds a top-level field based on existing fields. The mapping function is called with the qualified field names
227 /// of the provided field and the subfields. It should return the qualified field names used as a mapping source.
228 /// Projected fields can only be used for models used to write data.
229 RResult<void> AddProjectedField(std::unique_ptr<Detail::RFieldBase> field,
230 std::function<std::string(const std::string &)> mapping);
231
232 template <typename T>
233 T *Get(std::string_view fieldName) const
234 {
236 return fDefaultEntry->Get<T>(fieldName);
237 }
238
240
241 void Freeze();
242 bool IsFrozen() const { return fModelId != 0; }
243 std::uint64_t GetModelId() const { return fModelId; }
244
245 /// Ingests a model for a sub collection and attaches it to the current model
246 ///
247 /// Throws an exception if collectionModel is null.
248 std::shared_ptr<RCollectionNTupleWriter> MakeCollection(
249 std::string_view fieldName,
250 std::unique_ptr<RNTupleModel> collectionModel);
251
252 std::unique_ptr<REntry> CreateEntry() const;
253 /// In a bare entry, all values point to nullptr. The resulting entry shall use CaptureValueUnsafe() in order
254 /// set memory addresses to be serialized / deserialized
255 std::unique_ptr<REntry> CreateBareEntry() const;
256 REntry *GetDefaultEntry() const;
257
258 RFieldZero *GetFieldZero() const { return fFieldZero.get(); }
259 const Detail::RFieldBase *GetField(std::string_view fieldName) const;
260
261 std::string GetDescription() const { return fDescription; }
262 void SetDescription(std::string_view description);
263};
264
265} // namespace Experimental
266} // namespace ROOT
267
268#endif
#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:303
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
The REntry is a collection of values in an ntuple corresponding to a complete row in the data set.
Definition: REntry.hxx:43
Base class for all ROOT issued exceptions.
Definition: RError.hxx:78
The container field for an ntuple model, which itself has no physical representation.
Definition: RField.hxx:372
Projected fields are fields whose columns are reused from existing fields.
FieldMap_t fFieldMap
Maps the source fields from fModel to the target projected fields attached to fFieldZero.
std::unordered_map< const Detail::RFieldBase *, const Detail::RFieldBase * > FieldMap_t
The map keys are the projected target fields, the map values are the backing source fields Note that ...
RProjectedFields & operator=(RProjectedFields &&)=default
RProjectedFields(std::unique_ptr< RFieldZero > fieldZero)
const RNTupleModel * fModel
The model this set of projected fields belongs to.
RResult< void > Add(std::unique_ptr< Detail::RFieldBase > field, const FieldMap_t &fieldMap)
Adds a new projected field.
std::unique_ptr< RFieldZero > fFieldZero
The projected fields are attached to this zero field.
RProjectedFields(const RProjectedFields &)=delete
RProjectedFields & operator=(const RProjectedFields &)=delete
RResult< void > EnsureValidMapping(const Detail::RFieldBase *target, const FieldMap_t &fieldMap)
Asserts that the passed field is a valid target of the source field provided in the field map.
std::unique_ptr< RProjectedFields > Clone(const RNTupleModel *newModel) const
The new model needs to be a clone of fModel.
const Detail::RFieldBase * GetSourceField(const Detail::RFieldBase *target) const
The RNTupleModel encapulates the schema of an ntuple.
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 NTuple model.
std::uint64_t fModelId
Upon freezing, every model has a unique ID to distingusish it from other models.
std::uint64_t GetModelId() const
RNTupleModel(const RNTupleModel &)=delete
std::string GetDescription() const
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.
std::unique_ptr< REntry > CreateBareEntry() const
In a bare entry, all values point to nullptr.
std::unique_ptr< REntry > CreateEntry() const
static std::unique_ptr< RNTupleModel > Create()
void AddField(std::string_view fieldName, T *fromWhere)
Throws an exception if fromWhere is null.
std::unique_ptr< RProjectedFields > fProjectedFields
The set of projected top-level fields.
void SetDescription(std::string_view description)
std::unique_ptr< REntry > fDefaultEntry
Contains field values corresponding to the created top-level fields.
std::shared_ptr< T > MakeField(std::string_view fieldName, ArgsT &&... args)
Creates a new field and a corresponding tree value that is managed by a shared pointer.
std::shared_ptr< T > MakeField(std::pair< std::string_view, std::string_view > fieldNameDesc, ArgsT &&... args)
Creates a new field given a {name, description} pair and a corresponding tree value that is managed b...
std::shared_ptr< RCollectionNTupleWriter > MakeCollection(std::string_view fieldName, std::unique_ptr< RNTupleModel > collectionModel)
Ingests a model for a sub collection and attaches it to the current model.
RResult< void > AddProjectedField(std::unique_ptr< Detail::RFieldBase > field, std::function< std::string(const std::string &)> mapping)
Adds a top-level field based on existing fields.
const Detail::RFieldBase * GetField(std::string_view fieldName) const
RFieldZero * GetFieldZero() const
static std::unique_ptr< RNTupleModel > CreateBare()
A bare model has no default entry.
T * Get(std::string_view fieldName) const
const RProjectedFields & GetProjectedFields() const
void AddField(std::pair< std::string_view, std::string_view > fieldNameDesc, T *fromWhere)
Throws an exception if fromWhere is null.
RNTupleModel & operator=(const RNTupleModel &)=delete
void AddField(std::unique_ptr< Detail::RFieldBase > field)
Adds a field whose type is not known at compile time.
std::unique_ptr< RFieldZero > fFieldZero
Hierarchy of fields consisting of simple types and collections (sub trees)
RResult<void> has no data member and no Inspect() method but instead a Success() factory method.
Definition: RError.hxx:269
basic_string_view< char > string_view
double T(double x)
Definition: ChebyshevPol.h:34
void function(const Char_t *name_, T fun, const Char_t *docstring=0)
Definition: RExports.h:167
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.