PODIO v00-16-03
An Event-Data-Model Toolkit for High Energy Physics Experiments
Loading...
Searching...
No Matches
Frame.h
Go to the documentation of this file.
1#ifndef PODIO_FRAME_H
2#define PODIO_FRAME_H
3
9
10#include <initializer_list>
11#include <memory>
12#include <mutex>
13#include <optional>
14#include <set>
15#include <string>
16#include <type_traits>
17#include <unordered_map>
18#include <vector>
19
20namespace podio {
21
22/// Alias template for enabling overloads only for Collections
23template <typename T>
24using EnableIfCollection = typename std::enable_if_t<isCollection<T>>;
25
26/// Alias template for enabling overloads only for Collection r-values
27template <typename T>
28using EnableIfCollectionRValue = typename std::enable_if_t<isCollection<T> && !std::is_lvalue_reference_v<T>>;
29
30/// Alias template for enabling overloads for r-values
31template <typename T>
32using EnableIfRValue = typename std::enable_if_t<!std ::is_lvalue_reference_v<T>>;
33
34namespace detail {
35 /** The minimal interface for raw data types
36 */
39 return {};
40 }
41
42 std::optional<podio::CollectionReadBuffers> getCollectionBuffers(const std::string&) {
43 return std::nullopt;
44 }
45
46 /** Get the still available, i.e. yet unpacked, collections from the raw data
47 */
48 std::vector<std::string> getAvailableCollections() const {
49 return {};
50 }
51
52 /** Get the parameters that are stored in the raw data
53 */
54 std::unique_ptr<podio::GenericParameters> getParameters() {
55 return std::make_unique<podio::GenericParameters>();
56 }
57 };
58} // namespace detail
59
60template <typename FrameDataT>
61std::optional<podio::CollectionReadBuffers> unpack(FrameDataT* data, const std::string& name) {
62 return data->getCollectionBuffers(name);
63}
64
65/**
66 * Frame class that serves as a container of collection and meta data.
67 */
68class Frame {
69 /**
70 * Internal abstract interface for the type-erased implementation of the Frame
71 * class
72 */
73 struct FrameConcept {
74 virtual ~FrameConcept() = default;
75 virtual const podio::CollectionBase* get(const std::string& name) const = 0;
76 virtual const podio::CollectionBase* put(std::unique_ptr<podio::CollectionBase> coll, const std::string& name) = 0;
77 virtual podio::GenericParameters& parameters() = 0;
78 virtual const podio::GenericParameters& parameters() const = 0;
79
80 virtual std::vector<std::string> availableCollections() const = 0;
81
82 // Writing interface. Need this to be able to store all necessary information
83 // TODO: Figure out whether this can be "hidden" somehow
84 virtual podio::CollectionIDTable getIDTable() const = 0;
85 };
86
87 /**
88 * The interface implementation of the abstract FrameConcept that is necessary
89 * for a type-erased implementation of the Frame class
90 */
91 template <typename FrameDataT>
92 struct FrameModel final : FrameConcept, public ICollectionProvider {
93
94 FrameModel(std::unique_ptr<FrameDataT> data);
95 ~FrameModel() = default;
96 FrameModel(const FrameModel&) = delete;
97 FrameModel& operator=(const FrameModel&) = delete;
98 FrameModel(FrameModel&&) = default;
99 FrameModel& operator=(FrameModel&&) = default;
100
101 /** Try and get the collection from the internal storage and return a
102 * pointer to it if found. Otherwise return a nullptr
103 */
104 const podio::CollectionBase* get(const std::string& name) const final;
105
106 /** Try and place the collection into the internal storage and return a
107 * pointer to it. If a collection already exists or insertion fails, return
108 * a nullptr
109 */
110 const podio::CollectionBase* put(std::unique_ptr<CollectionBase> coll, const std::string& name) final;
111
112 /** Get a reference to the internally used GenericParameters
113 */
114 podio::GenericParameters& parameters() override {
115 return *m_parameters;
116 }
117 /** Get a const reference to the internally used GenericParameters
118 */
119 const podio::GenericParameters& parameters() const override {
120 return *m_parameters;
121 };
122
123 bool get(int collectionID, podio::CollectionBase*& collection) const override;
124
125 podio::CollectionIDTable getIDTable() const override {
126 // Make a copy
127 return {m_idTable.ids(), m_idTable.names()};
128 }
129
130 std::vector<std::string> availableCollections() const override;
131
132 private:
133 podio::CollectionBase* doGet(const std::string& name, bool setReferences = true) const;
134
135 using CollectionMapT = std::unordered_map<std::string, std::unique_ptr<podio::CollectionBase>>;
136
137 mutable CollectionMapT m_collections{}; ///< The internal map for storing unpacked collections
138 mutable std::unique_ptr<std::mutex> m_mapMtx{nullptr}; ///< The mutex for guarding the internal collection map
139 std::unique_ptr<FrameDataT> m_data{nullptr}; ///< The raw data read from file
140 mutable std::unique_ptr<std::mutex> m_dataMtx{nullptr}; ///< The mutex for guarding the raw data
141 podio::CollectionIDTable m_idTable{}; ///< The collection ID table
142 std::unique_ptr<podio::GenericParameters> m_parameters{nullptr}; ///< The generic parameter store for this frame
143 mutable std::set<int> m_retrievedIDs{}; ///< The IDs of the collections that we have already read (but not yet put
144 ///< into the map)
145 };
146
147 std::unique_ptr<FrameConcept> m_self; ///< The internal concept pointer through which all the work is done
148
149public:
150 /** Empty Frame constructor
151 */
152 Frame();
153
154 /** Frame constructor from (almost) arbitrary raw data
155 */
156 template <typename FrameDataT>
157 Frame(std::unique_ptr<FrameDataT>);
158
159 /** Frame constructor from (almost) arbitrary raw data.
160 *
161 * This r-value overload is mainly present for enabling the python bindings,
162 * where cppyy seems to strip the std::unique_ptr somewhere in the process
163 */
164 template <typename FrameDataT, typename = EnableIfRValue<FrameDataT>>
165 Frame(FrameDataT&&);
166
167 // The frame is a non-copyable type
168 Frame(const Frame&) = delete;
169 Frame& operator=(const Frame&) = delete;
170
171 Frame(Frame&&) = default;
172 Frame& operator=(Frame&&) = default;
173
174 /** Frame destructor */
175 ~Frame() = default;
176
177 /** Get a collection from the Frame
178 */
179 template <typename CollT, typename = EnableIfCollection<CollT>>
180 const CollT& get(const std::string& name) const;
181
182 /** Get a collection from the Frame. This is the pointer-to-base version for
183 * type-erased access (e.g. python interface)
184 */
185 const podio::CollectionBase* get(const std::string& name) const;
186
187 /** (Destructively) move a collection into the Frame and get a const reference
188 * back for further use
189 */
190 template <typename CollT, typename = EnableIfCollectionRValue<CollT>>
191 const CollT& put(CollT&& coll, const std::string& name);
192
193 /** Move a collection into the Frame handing over ownership to the Frame
194 */
195 void put(std::unique_ptr<podio::CollectionBase> coll, const std::string& name);
196
197 /** Add a value to the parameters of the Frame (if the type is supported).
198 * Copy the value into the internal store
199 */
200 template <typename T, typename = podio::EnableIfValidGenericDataType<T>>
201 inline void putParameter(const std::string& key, T value) {
202 m_self->parameters().setValue(key, value);
203 }
204
205 /** Add a string value to the parameters of the Frame by copying it. Dedicated
206 * overload for enabling the on-the-fly conversion on the string literals.
207 */
208 inline void putParameter(const std::string& key, std::string value) {
209 putParameter<std::string>(key, std::move(value));
210 }
211
212 /** Add a vector of strings to the parameters of the Frame (via copy).
213 * Dedicated overload for enabling on-the-fly conversions of initializer_list
214 * of string literals.
215 */
216 inline void putParameter(const std::string& key, std::vector<std::string> values) {
217 putParameter<std::vector<std::string>>(key, std::move(values));
218 }
219
220 /** Add a vector of values into the parameters of the Frame. Overload for
221 * catching on-the-fly conversions of initializer_lists of values.
222 */
223 template <typename T, typename = std::enable_if_t<detail::isInTuple<T, SupportedGenericDataTypes>>>
224 inline void putParameter(const std::string& key, std::initializer_list<T>&& values) {
225 putParameter<std::vector<T>>(key, std::move(values));
226 }
227
228 /** Retrieve parameters via key from the internal store. Return type will
229 * either by a const reference or a value depending on the desired type.
230 */
231 template <typename T, typename = podio::EnableIfValidGenericDataType<T>>
232 inline podio::GenericDataReturnType<T> getParameter(const std::string& key) const {
233 return m_self->parameters().getValue<T>(key);
234 }
235
236 /** Get all parameters that are stored in this Frame
237 */
239 return m_self->parameters();
240 }
241
242 /** Get the keys of all stored parameters for a given type
243 */
244 template <typename T, typename = podio::EnableIfValidGenericDataType<T>>
245 inline std::vector<std::string> getParameterKeys() const {
246 return m_self->parameters().getKeys<T>();
247 }
248
249 /** Get all **currently** available collections (including potentially
250 * unpacked ones from raw data)
251 */
252 std::vector<std::string> getAvailableCollections() const {
253 return m_self->availableCollections();
254 }
255
256 // Interfaces for writing below
257 // TODO: Hide this from the public interface somehow?
258
259 /**
260 * Get the GenericParameters for writing
261 */
262 [[deprecated("use getParameters instead")]] const podio::GenericParameters& getGenericParametersForWrite() const {
263 return m_self->parameters();
264 }
265
266 /**
267 * Get a collection for writing (in a prepared and "ready-to-write" state)
268 */
269 const podio::CollectionBase* getCollectionForWrite(const std::string& name) const {
270 const auto* coll = m_self->get(name);
271 if (coll) {
272 coll->prepareForWrite();
273 }
274
275 return coll;
276 }
277
279 return m_self->getIDTable();
280 }
281};
282
283// implementations below
284
285inline Frame::Frame() : Frame(std::make_unique<detail::EmptyFrameData>()) {
286}
287
288template <typename FrameDataT>
289Frame::Frame(std::unique_ptr<FrameDataT> data) : m_self(std::make_unique<FrameModel<FrameDataT>>(std::move(data))) {
290}
291
292template <typename FrameDataT, typename>
293Frame::Frame(FrameDataT&& data) : Frame(std::make_unique<FrameDataT>(std::move(data))) {
294}
295
296template <typename CollT, typename>
297const CollT& Frame::get(const std::string& name) const {
298 const auto* coll = dynamic_cast<const CollT*>(m_self->get(name));
299 if (coll) {
300 return *coll;
301 }
302 // TODO: Handle non-existing collections
303 static const auto emptyColl = CollT();
304 return emptyColl;
305}
306
307inline const podio::CollectionBase* Frame::get(const std::string& name) const {
308 return m_self->get(name);
309}
310
311inline void Frame::put(std::unique_ptr<podio::CollectionBase> coll, const std::string& name) {
312 const auto* retColl = m_self->put(std::move(coll), name);
313 if (!retColl) {
314 // TODO: Handle collisions
315 }
316}
317
318template <typename CollT, typename>
319const CollT& Frame::put(CollT&& coll, const std::string& name) {
320 const auto* retColl = static_cast<const CollT*>(m_self->put(std::make_unique<CollT>(std::move(coll)), name));
321 if (retColl) {
322 return *retColl;
323 }
324 // TODO: Handle collision case
325 static const auto emptyColl = CollT();
326 return emptyColl;
327}
328
329template <typename FrameDataT>
330Frame::FrameModel<FrameDataT>::FrameModel(std::unique_ptr<FrameDataT> data) :
331 m_mapMtx(std::make_unique<std::mutex>()),
332 m_data(std::move(data)),
333 m_dataMtx(std::make_unique<std::mutex>()),
334 m_idTable(std::move(m_data->getIDTable())),
335 m_parameters(std::move(m_data->getParameters())) {
336}
337
338template <typename FrameDataT>
339const podio::CollectionBase* Frame::FrameModel<FrameDataT>::get(const std::string& name) const {
340 return doGet(name);
341}
342
343template <typename FrameDataT>
344podio::CollectionBase* Frame::FrameModel<FrameDataT>::doGet(const std::string& name, bool setReferences) const {
345 {
346 // First check whether the collection is in the map already
347 //
348 // Collections only land here if they are fully unpacked, i.e.
349 // prepareAfterRead has been called or it has been put into the Frame
350 std::lock_guard lock{*m_mapMtx};
351 if (const auto it = m_collections.find(name); it != m_collections.end()) {
352 return it->second.get();
353 }
354 }
355
356 podio::CollectionBase* retColl = nullptr;
357
358 // Now try to get it from the raw data if we have the possibility
359 if (m_data) {
360 // Have the buffers in the outer scope here to hold the raw data lock as
361 // briefly as possible
362 auto buffers = std::optional<podio::CollectionReadBuffers>{std::nullopt};
363 {
364 std::lock_guard lock{*m_dataMtx};
365 buffers = unpack(m_data.get(), name);
366 }
367 if (buffers) {
368 auto coll = buffers->createCollection(buffers.value(), buffers->data == nullptr);
369 coll->prepareAfterRead();
370 coll->setID(m_idTable.collectionID(name));
371 {
372 std::lock_guard mapLock{*m_mapMtx};
373 auto [it, success] = m_collections.emplace(name, std::move(coll));
374 // TODO: Check success? Or simply assume that everything is fine at this point?
375 // TODO: Collision handling?
376 retColl = it->second.get();
377 }
378
379 if (setReferences) {
380 retColl->setReferences(this);
381 }
382 }
383 }
384
385 return retColl;
386}
387
388template <typename FrameDataT>
389bool Frame::FrameModel<FrameDataT>::get(int collectionID, CollectionBase*& collection) const {
390 const auto& name = m_idTable.name(collectionID);
391 const auto& [_, inserted] = m_retrievedIDs.insert(collectionID);
392
393 if (inserted) {
394 auto coll = doGet(name);
395 if (coll) {
396 collection = coll;
397 return true;
398 }
399 } else {
400 auto coll = doGet(name, false);
401 if (coll) {
402 collection = coll;
403 return true;
404 }
405 }
406
407 return false;
408}
409
410template <typename FrameDataT>
411const podio::CollectionBase* Frame::FrameModel<FrameDataT>::put(std::unique_ptr<podio::CollectionBase> coll,
412 const std::string& name) {
413 {
414 std::lock_guard lock{*m_mapMtx};
415 auto [it, success] = m_collections.try_emplace(name, std::move(coll));
416 if (success) {
417 // TODO: Check whether this collection is already known to the idTable
418 // -> What to do on collision?
419 // -> Check before we emplace it into the internal map to prevent possible
420 // collisions from collections that are potentially present from rawdata?
421 it->second->setID(m_idTable.add(name));
422 return it->second.get();
423 }
424 }
425
426 return nullptr;
427}
428
429template <typename FrameDataT>
430std::vector<std::string> Frame::FrameModel<FrameDataT>::availableCollections() const {
431 // TODO: Check if there is a more efficient way to do this. Currently this is
432 // done very conservatively, but in a way that should always work, regardless
433 // of assumptions. It might be possible to simply return what is in the
434 // idTable here, because that should in principle encompass everything that is
435 // in the raw data as well as things that have been put into the frame
436
437 // Lock both the internal map and the rawdata for this
438 std::scoped_lock lock{*m_mapMtx, *m_dataMtx};
439
440 auto collections = m_data->getAvailableCollections();
441 collections.reserve(collections.size() + m_collections.size());
442
443 for (const auto& [name, _] : m_collections) {
444 collections.push_back(name);
445 }
446
447 return collections;
448}
449
450} // namespace podio
451
452#endif // PODIO_FRAME_H
virtual void setID(unsigned id)=0
set collection ID
virtual void prepareForWrite() const =0
prepare buffers for serialization
virtual bool setReferences(const ICollectionProvider *collectionProvider)=0
initialize references after read
const std::vector< int > & ids() const
return the ids
const CollT & get(const std::string &name) const
Definition: Frame.h:297
const CollT & put(CollT &&coll, const std::string &name)
Definition: Frame.h:319
Frame & operator=(const Frame &)=delete
Frame & operator=(Frame &&)=default
podio::CollectionIDTable getCollectionIDTableForWrite() const
Definition: Frame.h:278
std::vector< std::string > getParameterKeys() const
Definition: Frame.h:245
Frame(const Frame &)=delete
std::vector< std::string > getAvailableCollections() const
Definition: Frame.h:252
Frame(Frame &&)=default
void putParameter(const std::string &key, std::string value)
Definition: Frame.h:208
const podio::GenericParameters & getGenericParametersForWrite() const
Definition: Frame.h:262
void putParameter(const std::string &key, std::initializer_list< T > &&values)
Definition: Frame.h:224
const podio::GenericParameters & getParameters() const
Definition: Frame.h:238
void putParameter(const std::string &key, std::vector< std::string > values)
Definition: Frame.h:216
podio::GenericDataReturnType< T > getParameter(const std::string &key) const
Definition: Frame.h:232
~Frame()=default
void putParameter(const std::string &key, T value)
Definition: Frame.h:201
const podio::CollectionBase * getCollectionForWrite(const std::string &name) const
Definition: Frame.h:269
typename std::enable_if_t< isCollection< T > > EnableIfCollection
Alias template for enabling overloads only for Collections.
Definition: Frame.h:24
typename std::enable_if_t<!std ::is_lvalue_reference_v< T > > EnableIfRValue
Alias template for enabling overloads for r-values.
Definition: Frame.h:32
std::optional< podio::CollectionReadBuffers > unpack(FrameDataT *data, const std::string &name)
Definition: Frame.h:61
typename detail::GenericDataReturnTypeHelper< T >::type GenericDataReturnType
typename std::enable_if_t< isCollection< T > &&!std::is_lvalue_reference_v< T > > EnableIfCollectionRValue
Alias template for enabling overloads only for Collection r-values.
Definition: Frame.h:28
std::unique_ptr< podio::GenericParameters > getParameters()
Definition: Frame.h:54
std::optional< podio::CollectionReadBuffers > getCollectionBuffers(const std::string &)
Definition: Frame.h:42
std::vector< std::string > getAvailableCollections() const
Definition: Frame.h:48
podio::CollectionIDTable getIDTable() const
Definition: Frame.h:38