diff --git a/CMakeLists.txt b/CMakeLists.txt index bc30d6539..99fd96e2f 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ project(podio) #--- Version ------------------------------------------------------------------- SET( ${PROJECT_NAME}_VERSION_MAJOR 0 ) SET( ${PROJECT_NAME}_VERSION_MINOR 17 ) -SET( ${PROJECT_NAME}_VERSION_PATCH 3 ) +SET( ${PROJECT_NAME}_VERSION_PATCH 99 ) SET( ${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}" ) diff --git a/doc/examples.md b/doc/examples.md index 6e08dbe0e..49f91b856 100644 --- a/doc/examples.md +++ b/doc/examples.md @@ -1,18 +1,23 @@ # Examples for Supported Interface -The following snippets show the support of PODIO for the different use cases. - The event `store` used in the examples is just an example implementation, and has to be replaced with the store used in the framework of your choice. +The following snippets show the support of PODIO for the different use cases as +well as some potential pitfalls. These examples are mainly concerned with how +collections of objects and the objects themselve interact. As such they are +framework agnostic. ### Object Ownership Every data created is either explicitly owned by collections or automatically garbage-collected. There is no need for any `new` or `delete` call on user side. +As a general rule: **If an object has been added to a collection, the collection +assumes ownership and if the collection goes out of scope all handles that point +to objects in this collection are invalidated as well.** ### Object Creation and Storage Objects and collections can be created via factories, which ensure proper object ownership: ```cpp - auto& hits = store.create("hits") + auto hits = HitCollection{}; auto hit1 = hits.create(1.4,2.4,3.7,4.2); // init with values auto hit2 = hits.create(); // default-construct object hit2.energy(42.23); @@ -36,10 +41,10 @@ In this respect all objects behave like objects in Python. The library supports the creation of one-to-many relations: ```cpp - auto& hits = store.create("hits"); + auto hits = HitCollection{}; auto hit1 = hits.create(); auto hit2 = hits.create(); - auto& clusters = store.create("clusters"); + auto clusters = ClusterCollection{}; auto cluster = clusters.create(); cluster.addHit(hit1); cluster.addHit(hit2); diff --git a/include/podio/ObjBase.h b/include/podio/ObjBase.h deleted file mode 100644 index 82504063a..000000000 --- a/include/podio/ObjBase.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef PODIO_OBJBASE_H -#define PODIO_OBJBASE_H - -#include "podio/ObjectID.h" -#include -#include - -namespace podio { - -class ObjBase { -public: - /// Constructor from ObjectID and initial object-count - ObjBase(ObjectID id_, unsigned i) : id(id_), ref_counter(i) { - } - - /// checks whether object is "untracked" by a collection - /// if yes, increases reference count - void acquire() { - if (id.index == podio::ObjectID::untracked) { - ++ref_counter; - } - }; - - /// checks whether object is "untracked" by a collection - /// if yes, decrease reference count and delete itself if count===0 - int release() { - if (id.index != podio::ObjectID::untracked) { - return 1; - }; - if (--ref_counter == 0) { - delete this; - } - return 0; - }; - - /// destructor - virtual ~ObjBase() = default; - -public: - /// ID of the object - podio::ObjectID id; - -private: - /// reference counter - std::atomic ref_counter; -}; - -} // namespace podio - -#endif diff --git a/include/podio/utilities/MaybeSharedPtr.h b/include/podio/utilities/MaybeSharedPtr.h new file mode 100644 index 000000000..436c16d53 --- /dev/null +++ b/include/podio/utilities/MaybeSharedPtr.h @@ -0,0 +1,171 @@ +#ifndef PODIO_UTILITIES_MAYBESHAREDPTR_H +#define PODIO_UTILITIES_MAYBESHAREDPTR_H + +#include + +namespace podio::utils { + +namespace detail { + /// Tag struct to create MaybeSharedPtr instances that initially own their + /// managed pointer and hence will be created with a control block (ownership of + /// the managed pointer may still change later!) + struct MarkOwnedTag {}; +} // namespace detail + +constexpr static auto MarkOwned [[maybe_unused]] = detail::MarkOwnedTag{}; + +/// "Semi-smart" pointer class for pointers that at some point during their +/// lifetime might hand over management to another entitity. E.g. Objects that +/// are added to a collection will hand over the management of their Obj* to +/// collection. In such a case two things need to be considered: +/// - Other Objects with the same Obj* instance should not delete the managed +/// Obj*, even if the last Object goes out of scope +/// - Even if the managed Obj* is gone (e.g. collection has gone out of scope or +/// was cleared), the remaining Object instances should still be able to +/// gracefully destruct, even if they are at this point merely an "empty husk" +/// The MaybeSharedPtr achieves this by having an optional control block that +/// controls the lifetime of itself and potentially the managed Obj*. +template +class MaybeSharedPtr { +public: + /// There are no empty MaybeSharedPtrs + MaybeSharedPtr() = delete; + + /// Constructor from raw pointer. Assumes someone else manages the pointer + /// already + explicit MaybeSharedPtr(T* p) : m_ptr(p) { + } + + /// Constructor from a raw pointer assuming ownership in the process + explicit MaybeSharedPtr(T* p, detail::MarkOwnedTag) : m_ptr(p), m_ctrlBlock(new ControlBlock()) { + } + + /// Copy constructor + MaybeSharedPtr(const MaybeSharedPtr& other) : m_ptr(other.m_ptr), m_ctrlBlock(other.m_ctrlBlock) { + // Increase the reference count if there is a control block + m_ctrlBlock && m_ctrlBlock->count++; + } + + /// Assignment operator + MaybeSharedPtr& operator=(MaybeSharedPtr other) { + swap(*this, other); + return *this; + } + + /// Move constructor + MaybeSharedPtr(MaybeSharedPtr&& other) : m_ptr(other.m_ptr), m_ctrlBlock(other.m_ctrlBlock) { + other.m_ptr = nullptr; + other.m_ctrlBlock = nullptr; + } + + /// Destructor + ~MaybeSharedPtr() { + // Only if we have a control block, do we assume that we have any + // responsibility in cleaning things up + if (m_ctrlBlock && --m_ctrlBlock->count == 0) { + // When the reference count reaches 0 we have to clean up control block in + // any case, but first we have to find out whether we also need to clean + // up the "managed" pointer + if (m_ctrlBlock->owned) { + delete m_ptr; + } + delete m_ctrlBlock; + } + } + + /// Get a raw pointer to the managed pointer. Do not change anything + /// concerning the management of the pointer + T* get() const { + return m_ptr; + } + + /// Get a raw pointer to the managed pointer and assume ownership. + T* release() { + if (m_ctrlBlock) { + // From now on we only need to keep track of the control block + m_ctrlBlock->owned = false; + } + return m_ptr; + } + + operator bool() const { + return m_ptr; + } + + T* operator->() { + return m_ptr; + } + const T* operator->() const { + return m_ptr; + } + + T& operator*() { + return *m_ptr; + } + const T& operator*() const { + return *m_ptr; + } + + template + friend void swap(MaybeSharedPtr& a, MaybeSharedPtr& b); + + // avoid a bit of typing for having all the necessary combinations of + // comparison operators +#define DECLARE_COMPARISON_OPERATOR(op) \ + template \ + friend bool operator op(const MaybeSharedPtr& lhs, const MaybeSharedPtr& rhs); \ + template \ + friend bool operator op(const MaybeSharedPtr& lhs, const U* rhs); \ + template \ + friend bool operator op(const U* lhs, const MaybeSharedPtr& rhs); + + DECLARE_COMPARISON_OPERATOR(==) + DECLARE_COMPARISON_OPERATOR(!=) + DECLARE_COMPARISON_OPERATOR(<) +#undef DECLARE_COMPARISON_OPERATOR + +private: + /// Simple control structure that controls the behavior of the + /// MaybeSharedPtr destructor. Keeps track of how many references of the + /// ControlBlock are currently still alive and whether the managed pointer + /// should be destructed alongside the ControlBlock, once the reference count + /// reaches 0. + struct ControlBlock { + std::atomic count{1}; ///< reference count + std::atomic owned{true}; ///< ownership flag for the managed pointer. true == we manage the pointer + }; + + T* m_ptr{nullptr}; + ControlBlock* m_ctrlBlock{nullptr}; +}; + +template +void swap(MaybeSharedPtr& a, MaybeSharedPtr& b) { + using std::swap; + swap(a.m_ptr, b.m_ptr); + swap(a.m_ctrlBlock, b.m_ctrlBlock); +} + +// helper macro for avoiding a bit of typing/repetition +#define DEFINE_COMPARISON_OPERATOR(op) \ + template \ + bool operator op(const MaybeSharedPtr& lhs, const MaybeSharedPtr& rhs) { \ + return lhs.m_ptr op rhs.m_ptr; \ + } \ + template \ + bool operator op(const MaybeSharedPtr& lhs, const U* rhs) { \ + return lhs.m_ptr op rhs; \ + } \ + template \ + bool operator op(const U* lhs, const MaybeSharedPtr& rhs) { \ + return lhs op rhs.m_ptr; \ + } + +DEFINE_COMPARISON_OPERATOR(==) +DEFINE_COMPARISON_OPERATOR(!=) +DEFINE_COMPARISON_OPERATOR(<) +#undef DEFINE_COMPARISON_OPERATOR + +} // namespace podio::utils + +#endif // PODIO_UTILITIES_MAYBESHAREDPTR_H diff --git a/python/templates/Collection.cc.jinja2 b/python/templates/Collection.cc.jinja2 index 93f691b06..91bdc6d7c 100644 --- a/python/templates/Collection.cc.jinja2 +++ b/python/templates/Collection.cc.jinja2 @@ -48,11 +48,11 @@ } Mutable{{ class.bare_type }} {{ collection_type }}::operator[](std::size_t index) { - return Mutable{{ class.bare_type }}(m_storage.entries[index]); + return Mutable{{ class.bare_type }}(podio::utils::MaybeSharedPtr(m_storage.entries[index])); } Mutable{{ class.bare_type }} {{ collection_type }}::at(std::size_t index) { - return Mutable{{ class.bare_type }}(m_storage.entries.at(index)); + return Mutable{{ class.bare_type }}(podio::utils::MaybeSharedPtr(m_storage.entries.at(index))); } std::size_t {{ collection_type }}::size() const { @@ -85,7 +85,7 @@ Mutable{{ class.bare_type }} {{ collection_type }}::create() { {% endif %} obj->id = {int(m_storage.entries.size() - 1), m_collectionID}; - return Mutable{{ class.bare_type }}(obj); + return Mutable{{ class.bare_type }}(podio::utils::MaybeSharedPtr(obj)); } void {{ collection_type }}::clear() { @@ -133,7 +133,7 @@ bool {{ collection_type }}::setReferences(const podio::ICollectionProvider* coll return m_storage.setReferences(collectionProvider, m_isSubsetColl); } -void {{ collection_type }}::push_back({{ class.bare_type }} object) { +void {{ collection_type }}::push_back(Mutable{{ class.bare_type }} object) { // We have to do different things here depending on whether this is a // subset collection or not. A normal collection cannot collect objects // that are already part of another collection, while a subset collection @@ -143,22 +143,29 @@ void {{ collection_type }}::push_back({{ class.bare_type }} object) { if (obj->id.index == podio::ObjectID::untracked) { const auto size = m_storage.entries.size(); obj->id = {(int)size, m_collectionID}; - m_storage.entries.push_back(obj); + m_storage.entries.push_back(obj.release()); {% if OneToManyRelations or VectorMembers %} - m_storage.createRelations(obj); + m_storage.createRelations(obj.get()); {% endif %} } else { throw std::invalid_argument("Object already in a collection. Cannot add it to a second collection"); } } else { - const auto obj = object.m_obj; - if (obj->id.index < 0) { - throw std::invalid_argument("Objects can only be stored in a subset collection if they are already elements of a collection"); - } - m_storage.entries.push_back(obj); - // No need to handle any relations here, since this is already done by the - // "owning" collection + + push_back({{ class.bare_type }}(object)); + } +} + +void {{ collection_type }}::push_back({{ class.bare_type }} object) { + if (!m_isSubsetColl) { + throw std::invalid_argument("Can only add immutable objects to subset collections"); + } + auto obj = object.m_obj; + if (obj->id.index < 0) { + // This path is only possible if we arrive here from an untracked Mutable object + throw std::invalid_argument("Object needs to be tracked by another collection in order for it to be storable in a subset collection"); } + m_storage.entries.push_back(obj.release()); } podio::CollectionWriteBuffers {{ collection_type }}::getBuffers() { diff --git a/python/templates/Collection.h.jinja2 b/python/templates/Collection.h.jinja2 index 616e1bfc8..2c5fa5d20 100644 --- a/python/templates/Collection.h.jinja2 +++ b/python/templates/Collection.h.jinja2 @@ -115,7 +115,9 @@ public: /// Append object to the collection - void push_back({{class.bare_type}} object); + void push_back(Mutable{{class.bare_type}} object); + /// Append an object to the (subset) collection + void push_back({{ class.bare_type }} object); void prepareForWrite() const final; void prepareAfterRead() final; @@ -194,7 +196,7 @@ Mutable{{ class.bare_type }} {{ class.bare_type }}Collection::create(Args&&... a {% endfor %} m_storage.createRelations(obj); {% endif %} - return Mutable{{ class.bare_type }}(obj); + return Mutable{{ class.bare_type }}(podio::utils::MaybeSharedPtr(obj)); } #if defined(PODIO_JSON_OUTPUT) && !defined(__CLING__) diff --git a/python/templates/MutableObject.h.jinja2 b/python/templates/MutableObject.h.jinja2 index 354db473d..086f99b8b 100644 --- a/python/templates/MutableObject.h.jinja2 +++ b/python/templates/MutableObject.h.jinja2 @@ -12,7 +12,9 @@ {% for include in includes %} {{ include }} {% endfor %} -#include "podio/ObjectID.h" + +#include "podio/utilities/MaybeSharedPtr.h" + #include #include @@ -36,6 +38,7 @@ public: using collection_type = {{ class.bare_type }}Collection; {{ macros.constructors_destructors(class.bare_type, Members, prefix='Mutable') }} + /// conversion to const object operator {{ class.bare_type }}() const; @@ -51,7 +54,10 @@ public: {{ macros.common_object_funcs(class.bare_type, prefix='Mutable') }} private: - {{ class.bare_type }}Obj* m_obj; + /// constructor from existing {{ class.bare_type }}Obj + explicit Mutable{{ class.bare_type }}(podio::utils::MaybeSharedPtr<{{ class.bare_type }}Obj> obj); + + podio::utils::MaybeSharedPtr<{{ class.bare_type }}Obj> m_obj{nullptr}; }; {{ macros.json_output(class.bare_type, prefix='Mutable') }} diff --git a/python/templates/Obj.cc.jinja2 b/python/templates/Obj.cc.jinja2 index d651c4ecf..1f935ca46 100644 --- a/python/templates/Obj.cc.jinja2 +++ b/python/templates/Obj.cc.jinja2 @@ -12,11 +12,12 @@ {%- endfor %} {%- endmacro -%} +#include "podio/ObjectID.h" {{ utils.namespace_open(class.namespace) }} {% with obj_type = class.bare_type + 'Obj' %} {{ obj_type }}::{{ obj_type }}() : -{% raw %} ObjBase{{}, 0}{% endraw %}, + id(), data(){{ single_relations_initialize(OneToOneRelations) }} {%- for relation in OneToManyRelations + VectorMembers %}, m_{{ relation.name }}(new std::vector<{{ relation.full_type }}>()) @@ -25,11 +26,11 @@ { } {{ obj_type }}::{{ obj_type }}(const podio::ObjectID id_, {{ class.bare_type }}Data data_) : - ObjBase{id_, 0}, data(data_) + id(id_), data(data_) { } {{ obj_type }}::{{ obj_type }}(const {{ obj_type }}& other) : -{% raw %} ObjBase{{}, 0}{% endraw %}, + id(), data(other.data){{ single_relations_initialize(OneToOneRelations) }} {%- for relation in OneToManyRelations + VectorMembers %}, m_{{ relation.name }}(new std::vector<{{ relation.full_type }}>(*(other.m_{{ relation.name }}))) diff --git a/python/templates/Obj.h.jinja2 b/python/templates/Obj.h.jinja2 index f7523a44d..f7a98ec38 100644 --- a/python/templates/Obj.h.jinja2 +++ b/python/templates/Obj.h.jinja2 @@ -10,7 +10,7 @@ {{ include }} {% endfor %} -#include "podio/ObjBase.h" +#include "podio/ObjectID.h" {% if OneToManyRelations or VectorMembers %} #include {%- endif %} @@ -21,7 +21,7 @@ class {{ class.bare_type }}; {% with obj_type = class.bare_type + 'Obj' %} -class {{ class.bare_type }}Obj : public podio::ObjBase { +class {{ class.bare_type }}Obj { public: /// constructor {{ obj_type }}(); @@ -39,6 +39,7 @@ public: {% endif %} public: + podio::ObjectID id; {{ class.bare_type }}Data data; {% for relation in OneToOneRelations %} {{ relation.full_type }}* m_{{ relation.name }}{nullptr}; diff --git a/python/templates/Object.cc.jinja2 b/python/templates/Object.cc.jinja2 index bbb8e8856..a526162ac 100644 --- a/python/templates/Object.cc.jinja2 +++ b/python/templates/Object.cc.jinja2 @@ -18,6 +18,12 @@ {{ macros.constructors_destructors(class.bare_type, Members) }} +{{ class.bare_type }}::{{ class.bare_type }}({{ class.bare_type }}Obj* obj) : m_obj(podio::utils::MaybeSharedPtr<{{ class.bare_type }}Obj>(obj)) {} + +{{ class.bare_type }} {{ class.bare_type }}::makeEmpty() { + return {nullptr}; +} + {{ macros.member_getters(class, Members, use_get_syntax) }} {{ macros.single_relation_getters(class, OneToOneRelations, use_get_syntax) }} {{ macros.multi_relation_handling(class, OneToManyRelations + VectorMembers, use_get_syntax) }} diff --git a/python/templates/Object.h.jinja2 b/python/templates/Object.h.jinja2 index 742f7bbdc..89b6127ce 100644 --- a/python/templates/Object.h.jinja2 +++ b/python/templates/Object.h.jinja2 @@ -10,7 +10,9 @@ {% for include in includes %} {{ include }} {% endfor %} -#include "podio/ObjectID.h" + +#include "podio/utilities/MaybeSharedPtr.h" + #include #include @@ -23,12 +25,14 @@ {{ utils.namespace_open(class.namespace) }} class Mutable{{ class.bare_type }}; class {{ class.bare_type }}Collection; +class {{ class.bare_type }}CollectionData; {{ macros.class_description(class.bare_type, Description, Author) }} class {{ class.bare_type }} { friend class Mutable{{ class.bare_type }}; friend class {{ class.bare_type }}Collection; + friend class {{ class.full_type }}CollectionData; friend class {{ class.bare_type }}CollectionIterator; public: @@ -37,6 +41,8 @@ public: {{ macros.constructors_destructors(class.bare_type, Members) }} + static {{ class.bare_type }} makeEmpty(); + public: {{ macros.member_getters(Members, use_get_syntax) }} @@ -46,7 +52,11 @@ public: {{ macros.common_object_funcs(class.bare_type) }} private: - {{ class.bare_type }}Obj* m_obj; + /// constructor from existing {{ class.bare_type }}Obj + explicit {{ class.bare_type}}(podio::utils::MaybeSharedPtr<{{ class.bare_type }}Obj> obj); + {{ class.bare_type}}({{ class.bare_type }}Obj* obj); + + podio::utils::MaybeSharedPtr<{{ class.bare_type }}Obj> m_obj{nullptr}; }; std::ostream& operator<<(std::ostream& o, const {{ class.bare_type }}& value); diff --git a/python/templates/macros/collections.jinja2 b/python/templates/macros/collections.jinja2 index cda1cf948..743efa7ec 100644 --- a/python/templates/macros/collections.jinja2 +++ b/python/templates/macros/collections.jinja2 @@ -68,14 +68,14 @@ std::vector<{{ member.full_type }}> {{ class.bare_type }}Collection::{{ member.n if (id.index != podio::ObjectID::invalid) { podio::CollectionBase* coll = nullptr; if (!collectionProvider->get(id.collectionID, coll)) { - m_rel_{{ relation.name }}->emplace_back(nullptr); + m_rel_{{ relation.name }}->emplace_back({{ relation.full_type }}::makeEmpty()); continue; } {{ relation.full_type }}Collection* tmp_coll = static_cast<{{ relation.full_type }}Collection*>(coll); const auto tmp = (*tmp_coll)[id.index]; m_rel_{{ relation.name }}->emplace_back(tmp); } else { - m_rel_{{ relation.name }}->emplace_back(nullptr); + m_rel_{{ relation.name }}->emplace_back({{ relation.full_type }}::makeEmpty()); } } {% endmacro %} diff --git a/python/templates/macros/declarations.jinja2 b/python/templates/macros/declarations.jinja2 index 762f7639c..109810a47 100644 --- a/python/templates/macros/declarations.jinja2 +++ b/python/templates/macros/declarations.jinja2 @@ -57,16 +57,15 @@ {% macro constructors_destructors(type, members, prefix='') %} {% set full_type = prefix + type %} /// default constructor - {{ full_type }}(); -{% if members %} - {{ full_type }}({{ members | map(attribute='signature') | join(', ') }}); -{% endif %} + {{ full_type }}(); - /// constructor from existing {{ type }}Obj - {{ full_type }}({{ type }}Obj* obj); +{%if members %} + /// Constructor initializing all members + {{ full_type }}({{ members | map(attribute='signature') | join(', ') }}); +{% endif %} /// copy constructor - {{ full_type }}(const {{ full_type }}& other); + {{ full_type }}(const {{ full_type }}& other) = default; /// copy-assignment operator {{ full_type }}& operator=({{ full_type }} other); @@ -75,7 +74,7 @@ Mutable{{ type }} clone() const; /// destructor - ~{{ full_type }}(); + ~{{ full_type }}() = default; {% endmacro %} @@ -84,7 +83,7 @@ /// check whether the object is actually available bool isAvailable() const; /// disconnect from {{ type }}Obj instance - void unlink() { m_obj = nullptr; } + void unlink() { m_obj = podio::utils::MaybeSharedPtr<{{ type }}Obj>{nullptr}; } {% set inverse_type = type if prefix else 'Mutable' + type %} bool operator==(const {{ full_type }}& other) const { return m_obj == other.m_obj; } diff --git a/python/templates/macros/implementations.jinja2 b/python/templates/macros/implementations.jinja2 index f5d7f9fb2..5324a918a 100644 --- a/python/templates/macros/implementations.jinja2 +++ b/python/templates/macros/implementations.jinja2 @@ -1,42 +1,29 @@ {% macro constructors_destructors(type, members, prefix='') %} {% set full_type = prefix + type %} -{{ full_type }}::{{ full_type }}() : m_obj(new {{ type }}Obj()) { - if (m_obj) m_obj->acquire(); -} + +{{ full_type }}::{{ full_type }}() : + m_obj(new {{ type }}Obj{}, podio::utils::MarkOwned) {} {% if members %} -{{ full_type }}::{{ full_type }}({{ members | map(attribute='signature') | join(', ') }}) : m_obj(new {{ type }}Obj()) { - m_obj->acquire(); +{{ full_type }}::{{ full_type }}({{ members | map(attribute='signature') | join(', ') }}) : + m_obj(new {{ type }}Obj{}, podio::utils::MarkOwned) { {% for member in members %} m_obj->data.{{ member.name }} = {{ member.name }}; {% endfor %} } {% endif %} -{{ full_type}}::{{ full_type }}(const {{ full_type }}& other) : m_obj(other.m_obj) { - if (m_obj) m_obj->acquire(); -} - {{ full_type }}& {{ full_type }}::operator=({{ full_type }} other) { swap(*this, other); return *this; } -{{ full_type }}::{{ full_type }}( {{ type }}Obj* obj) : m_obj(obj) { - if (m_obj) { - m_obj->acquire(); - } -} - Mutable{{ type }} {{ full_type }}::clone() const { - return {new {{ type }}Obj(*m_obj)}; + return Mutable{{ type }}(podio::utils::MaybeSharedPtr(new {{ type }}Obj(*m_obj), podio::utils::MarkOwned)); } -{{ full_type }}::~{{ full_type }}() { - if (m_obj) { - m_obj->release(); - } -} +{{ full_type }}::{{ full_type }}(podio::utils::MaybeSharedPtr<{{ type }}Obj> obj) : m_obj(obj) {} + {%- endmacro %} @@ -83,7 +70,7 @@ void {{ class_type }}::{{ sub_member.setter_name(get_syntax) }}({{ sub_member.fu {% for relation in relations %} const {{ relation.full_type }} {{ class_type }}::{{ relation.getter_name(get_syntax) }}() const { if (!m_obj->m_{{ relation.name }}) { - return {{ relation.full_type }}(nullptr); + return {{ relation.full_type }}::makeEmpty(); } return {{ relation.full_type }}(*(m_obj->m_{{ relation.name }})); } @@ -176,6 +163,9 @@ bool {{ full_type }}::operator==(const {{ inverse_type }}& other) const { {% macro ostream_operator(type, members, single_relations, multi_relations, get_syntax) %} std::ostream& operator<<(std::ostream& o, const {{ type }}& value) { + if (!value.isAvailable()) { + return o << "[not available]"; + } o << " id: " << value.id() << '\n'; {% for member in members %} {% if member.is_array %} diff --git a/python/templates/macros/iterator.jinja2 b/python/templates/macros/iterator.jinja2 index 4485504bd..470953666 100644 --- a/python/templates/macros/iterator.jinja2 +++ b/python/templates/macros/iterator.jinja2 @@ -1,8 +1,9 @@ {% macro iterator_declaration(class, prefix='') %} {% with iterator_type = class.bare_type + prefix + 'CollectionIterator' %} +{% set ptr_init = 'podio::utils::MaybeSharedPtr<' + class.bare_type +'Obj>{nullptr}' %} class {{ iterator_type }} { public: - {{ iterator_type }}(size_t index, const {{ class.bare_type }}ObjPointerContainer* collection) : m_index(index), m_object(nullptr), m_collection(collection) {} + {{ iterator_type }}(size_t index, const {{ class.bare_type }}ObjPointerContainer* collection) : m_index(index), m_object({{ ptr_init }}), m_collection(collection) {} {{ iterator_type }}(const {{ iterator_type }}&) = delete; {{ iterator_type }}& operator=(const {{ iterator_type }}&) = delete; @@ -30,13 +31,14 @@ private: {% macro iterator_definitions(class, prefix='') %} {% with iterator_type = class.bare_type + prefix + 'CollectionIterator' %} +{% set ptr_type = 'podio::utils::MaybeSharedPtr<' + class.bare_type +'Obj>' %} {{ prefix }}{{ class.bare_type }} {{ iterator_type }}::operator*() { - m_object.m_obj = (*m_collection)[m_index]; + m_object.m_obj = {{ ptr_type }}((*m_collection)[m_index]); return m_object; } {{ prefix }}{{ class.bare_type }}* {{ iterator_type }}::operator->() { - m_object.m_obj = (*m_collection)[m_index]; + m_object.m_obj = {{ ptr_type }}((*m_collection)[m_index]); return &m_object; } diff --git a/python/templates/selection.xml.jinja2 b/python/templates/selection.xml.jinja2 index 61d9a7a9c..00b52c78a 100644 --- a/python/templates/selection.xml.jinja2 +++ b/python/templates/selection.xml.jinja2 @@ -29,6 +29,7 @@ {# Otherwise the python interface does not work in gcc builds #} {# Additionally, in order to allow "direct" access to the user facing classes #} {# we have to declare them here, otherwise they cannot be easily imported #} +{{ class_selection(class, prefix='Mutable') }} {{ class_selection(class) }} {{ class_selection(class, postfix='Collection') }} {% endfor %} diff --git a/tests/CTestCustom.cmake b/tests/CTestCustom.cmake index f386ec07b..af55f1532 100644 --- a/tests/CTestCustom.cmake +++ b/tests/CTestCustom.cmake @@ -29,22 +29,12 @@ if ((NOT "@FORCE_RUN_ALL_TESTS@" STREQUAL "ON") AND (NOT "@USE_SANITIZER@" STREQ write_frame_root read_frame_root - write_sio - read_sio - read_and_write_sio - write_timed_sio - read_timed_sio check_benchmark_outputs_sio - write_frame_sio - read_frame_sio - read_frame_legacy_sio write_python_frame_sio read_python_frame_sio - read_and_write_frame_sio write_ascii - ostream_operator relation_range pyunittest @@ -79,4 +69,24 @@ if ((NOT "@FORCE_RUN_ALL_TESTS@" STREQUAL "ON") AND (NOT "@USE_SANITIZER@" STREQ if("@USE_SANITIZER@" MATCHES "Memory(WithOrigin)?") list(REMOVE_ITEM CTEST_CUSTOM_TESTS_IGNORE ostream_operator) endif() + + # There is still an (indirect) leak somewhere in the collection buffer + # machinery before they hit the collections. + # See also https://github.com/AIDASoft/podio/issues/506 + if("@USE_SANITIZER@" MATCHES "Address") + set(CTEST_CUSTOM_TESTS_IGNORE + ${CTEST_CUSTOM_TESTS_IGNORE} + + write_sio + read_sio + read_and_write_sio + write_timed_sio + read_timed_sio + write_frame_sio + read_frame_sio + read_frame_legacy_sio + read_and_write_frame_sio + ) + endif() + endif() diff --git a/tests/ostream_operator.cpp b/tests/ostream_operator.cpp index 25e44fb1d..cdd41e0df 100644 --- a/tests/ostream_operator.cpp +++ b/tests/ostream_operator.cpp @@ -1,15 +1,16 @@ -#include "datamodel/MutableExampleForCyclicDependency1.h" -#include "datamodel/MutableExampleForCyclicDependency2.h" -#include "datamodel/MutableExampleMC.h" -#include "datamodel/MutableExampleReferencingType.h" +#include "datamodel/ExampleForCyclicDependency1Collection.h" +#include "datamodel/ExampleForCyclicDependency2Collection.h" +#include "datamodel/ExampleMCCollection.h" +#include "datamodel/ExampleReferencingTypeCollection.h" #include // When using CTest for unit testing it is enough for this test to eventually // segfault int main(int, char**) { - MutableExampleMC mcp1; - MutableExampleMC mcp2; + ExampleMCCollection mcps{}; + auto mcp1 = mcps.create(); + auto mcp2 = mcps.create(); mcp1.adddaughters(mcp2); mcp2.addparents(mcp1); @@ -18,8 +19,10 @@ int main(int, char**) { std::cout << mcp1 << std::endl; // Make sure everything still works if the relation is not of the same type - MutableExampleForCyclicDependency1 cyc1; - MutableExampleForCyclicDependency2 cyc2; + ExampleForCyclicDependency1Collection cyc1Coll{}; + auto cyc1 = cyc1Coll.create(); + ExampleForCyclicDependency2Collection cyc2Coll{}; + auto cyc2 = cyc2Coll.create(); cyc1.ref(cyc2); cyc2.ref(cyc1); @@ -27,9 +30,10 @@ int main(int, char**) { std::cout << cyc1 << cyc2 << std::endl; // Non-cyclical references - MutableExampleReferencingType ref1; - MutableExampleReferencingType ref2; - ExampleReferencingType ref3; + ExampleReferencingTypeCollection refColl; + auto ref1 = refColl.create(); + auto ref2 = refColl.create(); + auto ref3 = refColl.create(); ref1.addRefs(ref2); ref2.addRefs(ref3); diff --git a/tests/read_python_frame.h b/tests/read_python_frame.h index 5a06cc4ce..ba1ed5269 100644 --- a/tests/read_python_frame.h +++ b/tests/read_python_frame.h @@ -16,15 +16,15 @@ int checkHits(const ExampleHitCollection& hits) { auto hit1 = hits[0]; if (hit1.cellID() != 0xbad || hit1.x() != 0.0 || hit1.y() != 0.0 || hit1.z() != 0.0 || hit1.energy() != 23.0) { - std::cerr << "Could not retrieve the correct hit[0]: (expected: " << ExampleHit(0xbad, 0.0, 0.0, 0.0, 23.0) + std::cerr << "Could not retrieve the correct hit[0]: (expected: " << MutableExampleHit(0xbad, 0.0, 0.0, 0.0, 23.0) << ", actual: " << hit1 << ")" << std::endl; return 1; } auto hit2 = hits[1]; if (hit2.cellID() != 0xcaffee || hit2.x() != 1.0 || hit2.y() != 0.0 || hit2.z() != 0.0 || hit2.energy() != 12.0) { - std::cerr << "Could not retrieve the correct hit[1]: (expected: " << ExampleHit(0xcaffee, 1.0, 0.0, 0.0, 12.0) - << ", actual: " << hit1 << ")" << std::endl; + std::cerr << "Could not retrieve the correct hit[1]: (expected: " + << MutableExampleHit(0xcaffee, 1.0, 0.0, 0.0, 12.0) << ", actual: " << hit1 << ")" << std::endl; return 1; } diff --git a/tests/root_io/read_and_write_associated.cpp b/tests/root_io/read_and_write_associated.cpp index 00388ea42..6cf4988ff 100644 --- a/tests/root_io/read_and_write_associated.cpp +++ b/tests/root_io/read_and_write_associated.cpp @@ -29,11 +29,8 @@ void writeCollection() { event.putParameter("UserEventName", std::to_string(i)); auto hits = ExampleHitCollection{}; - auto hit1 = ExampleHit(0xbad, 0., 0., 0., 23. + i); - auto hit2 = ExampleHit(0xcaffee, 1., 0., 0., 12. + i); - - hits.push_back(hit1); - hits.push_back(hit2); + auto hit1 = hits.create(0xbadULL, 0., 0., 0., 23. + i); + auto hit2 = hits.create(0xcaffeeULL, 1., 0., 0., 12. + i); // Clusters auto clusters = ExampleClusterCollection{}; diff --git a/tests/root_io/relation_range.cpp b/tests/root_io/relation_range.cpp index 0a7e3cb6d..3ca83fad7 100644 --- a/tests/root_io/relation_range.cpp +++ b/tests/root_io/relation_range.cpp @@ -158,8 +158,8 @@ void testExampleWithVectorMember() { void testExampleReferencingType() { MutableExampleReferencingType ex; - ExampleReferencingType ex1; - ExampleReferencingType ex2; + MutableExampleReferencingType ex1; + MutableExampleReferencingType ex2; ex.addRefs(ex1); ex.addRefs(ex2); diff --git a/tests/unittests/frame.cpp b/tests/unittests/frame.cpp index e76803fd7..cc6be08e1 100644 --- a/tests/unittests/frame.cpp +++ b/tests/unittests/frame.cpp @@ -5,8 +5,10 @@ #include "datamodel/ExampleClusterCollection.h" #include "datamodel/ExampleHitCollection.h" +#include #include #include +#include #include TEST_CASE("Frame collections", "[frame][basics]") { @@ -377,3 +379,50 @@ TEST_CASE("Frame double insert", "[frame][basics]") { event.put(std::move(clusters), "clusters"); REQUIRE_THROWS_AS(event.put(std::move(other_clusters), "clusters"), std::invalid_argument); } + +TEST_CASE("Frame destructor ASanFail") { + std::map> hitClusterMap{}; + podio::Frame frame{}; + + // Introduce a scope to mimic the DD4hep structure + { + for (const auto& name : {"foo", "bar", "baz"}) { + auto& hitClusters = hitClusterMap[name]; + + for (int i = 0; i < 3; ++i) { + auto cluster = hitClusters.first->create(); + + for (int j = 0; j < 5; ++j) { + auto hit = hitClusters.second->create(); + cluster.addHits(hit); + } + } + } + } + + // Introduce a commit scope + { + for (auto& [name, collections] : hitClusterMap) { + frame.put(std::move(collections.first), name + "Clusters"); + frame.put(std::move(collections.second), name + "Hits"); + } + frame = {}; + hitClusterMap.clear(); + } +} + +TEST_CASE("EIC-Jana2 cleanup use case", "[memory-management][492][174]") { + // Test case that only triggers in ASan builds if memory-management / cleanup + // has a bug + ExampleCluster* clone; + { + podio::Frame frame; + { + ExampleClusterCollection coll; + coll.create(); + const ExampleClusterCollection& moved = frame.put(std::move(coll), "mycoll"); + clone = new ExampleCluster(moved[0]); + } + } + delete clone; +} diff --git a/tests/unittests/unittest.cpp b/tests/unittests/unittest.cpp index 8724a25ea..35c936dae 100644 --- a/tests/unittests/unittest.cpp +++ b/tests/unittests/unittest.cpp @@ -12,6 +12,7 @@ #include "catch2/matchers/catch_matchers_vector.hpp" // podio specific includes +#include "datamodel/MutableExampleHit.h" #include "podio/EventStore.h" #include "podio/Frame.h" #include "podio/GenericParameters.h" @@ -78,9 +79,9 @@ TEST_CASE("Assignment-operator ref count", "[basics][memory-management]") { // Make sure that the assignment operator handles the reference count // correctly. (Will trigger in an ASan build if it is not the case) // See https://github.com/AIDASoft/podio/issues/200 - std::map hitMap; + std::map hitMap; for (int i = 0; i < 10; ++i) { - auto hit = ExampleHit(0x42ULL, i, i, i, i); + auto hit = MutableExampleHit(0x42ULL, i, i, i, i); hitMap[i] = hit; } @@ -91,6 +92,15 @@ TEST_CASE("Assignment-operator ref count", "[basics][memory-management]") { } } +TEST_CASE("ostream-operator", "[basics]") { + // Make sure that trying to print an object that is not available does not crash + auto hit = ExampleHit::makeEmpty(); + REQUIRE_FALSE(hit.isAvailable()); + std::stringstream sstr; + sstr << hit; + REQUIRE(sstr.str() == "[not available]"); +} + TEST_CASE("Clearing", "[UBSAN-FAIL][ASAN-FAIL][THREAD-FAIL][basics][memory-management]") { auto store = podio::EventStore(); auto& hits = store.create("hits"); @@ -101,7 +111,7 @@ TEST_CASE("Clearing", "[UBSAN-FAIL][ASAN-FAIL][THREAD-FAIL][basics][memory-manag hits.clear(); clusters.clear(); auto hit1 = hits.create(); - auto hit2 = ExampleHit(); + auto hit2 = MutableExampleHit(); hit1.energy(double(i)); auto cluster = clusters.create(); cluster.addHits(hit1); @@ -130,7 +140,8 @@ TEST_CASE("Cloning", "[basics][memory-management]") { auto cluster2 = cluster.clone(); cluster.addHits(hit2); // check that the clone of a const object is mutable - auto const_hit = ExampleHit(); + auto mutable_hit = MutableExampleHit(); + auto const_hit = ExampleHit(mutable_hit); auto const_hit_clone = const_hit.clone(); const_hit_clone.energy(30); REQUIRE(success); @@ -142,14 +153,38 @@ TEST_CASE("Component", "[basics]") { REQUIRE(3 == info.component().data.x); } -TEST_CASE("Cyclic", "[LEAK-FAIL][basics][relations][memory-management]") { - auto start = MutableExampleForCyclicDependency1(); - auto isAvailable = start.ref().isAvailable(); - REQUIRE_FALSE(isAvailable); - auto end = MutableExampleForCyclicDependency2(); +TEST_CASE("makeEmpty", "[basics]") { + auto hit = ExampleHit::makeEmpty(); + // Any access to such a handle is a crash + REQUIRE_FALSE(hit.isAvailable()); + + hit = MutableExampleHit{}; + REQUIRE(hit.isAvailable()); + REQUIRE(hit.energy() == 0); +} + +TEST_CASE("Cyclic", "[basics][relations][memory-management]") { + auto coll1 = ExampleForCyclicDependency1Collection(); + auto start = coll1.create(); + REQUIRE_FALSE(start.ref().isAvailable()); + auto coll2 = ExampleForCyclicDependency2Collection(); + auto end = coll2.create(); + start.ref(end); + REQUIRE(start.ref().isAvailable()); + end.ref(start); + REQUIRE(start == end.ref()); + auto end_eq = start.ref(); + auto start_eq = end_eq.ref(); + REQUIRE(start == start_eq); + REQUIRE(start == start.ref().ref()); +} + +TEST_CASE("Cyclic w/o collection", "[LEAK-FAIL][basics][relations][memory-management]") { + auto start = MutableExampleForCyclicDependency1{}; + REQUIRE_FALSE(start.ref().isAvailable()); + auto end = MutableExampleForCyclicDependency2{}; start.ref(end); - isAvailable = start.ref().isAvailable(); - REQUIRE(isAvailable); + REQUIRE(start.ref().isAvailable()); end.ref(start); REQUIRE(start == end.ref()); auto end_eq = start.ref(); @@ -158,11 +193,22 @@ TEST_CASE("Cyclic", "[LEAK-FAIL][basics][relations][memory-management]") { REQUIRE(start == start.ref().ref()); } -TEST_CASE("Invalid_refs", "[LEAK-FAIL][basics][relations]") { +TEST_CASE("Container lifetime", "[basics][memory-management]") { + std::vector hits; + { + MutableExampleHit hit; + hit.energy(3.14f); + hits.push_back(hit); + } + auto hit = hits[0]; + REQUIRE(hit.energy() == 3.14f); +} + +TEST_CASE("Invalid_refs", "[basics][relations]") { auto store = podio::EventStore(); auto& hits = store.create("hits"); auto hit1 = hits.create(0xcaffeeULL, 0., 0., 0., 0.); - auto hit2 = ExampleHit(); + auto hit2 = MutableExampleHit(); auto& clusters = store.create("clusters"); auto cluster = clusters.create(); cluster.addHits(hit1); @@ -220,7 +266,7 @@ TEST_CASE("Notebook", "[basics]") { TEST_CASE("OneToOneRelations", "[basics][relations]") { bool success = true; - auto cluster = ExampleCluster(); + auto cluster = MutableExampleCluster(); auto rel = MutableExampleWithOneRelation(); rel.cluster(cluster); REQUIRE(success); @@ -391,11 +437,11 @@ TEST_CASE("Extracode", "[basics][code-gen]") { } TEST_CASE("AssociativeContainer", "[basics]") { - auto clu1 = ExampleCluster(); - auto clu2 = ExampleCluster(); - auto clu3 = ExampleCluster(); - auto clu4 = ExampleCluster(); - auto clu5 = ExampleCluster(); + auto clu1 = MutableExampleCluster(); + auto clu2 = MutableExampleCluster(); + auto clu3 = MutableExampleCluster(); + auto clu4 = MutableExampleCluster(); + auto clu5 = MutableExampleCluster(); std::set cSet; cSet.insert(clu1); @@ -426,12 +472,19 @@ TEST_CASE("AssociativeContainer", "[basics]") { } TEST_CASE("Equality", "[basics]") { - auto cluster = ExampleCluster(); + auto cluster = MutableExampleCluster(); auto rel = MutableExampleWithOneRelation(); rel.cluster(cluster); auto returned_cluster = rel.cluster(); REQUIRE(cluster == returned_cluster); REQUIRE(returned_cluster == cluster); + + auto clu = ExampleCluster::makeEmpty(); + auto clu2 = ExampleCluster::makeEmpty(); + // Empty handles always compare equal + REQUIRE(clu == clu2); + // They never compare equal to a non-empty handle + REQUIRE(!(clu == cluster)); } TEST_CASE("UserInitialization", "[basics][code-gen]") { @@ -449,7 +502,7 @@ TEST_CASE("UserInitialization", "[basics][code-gen]") { REQUIRE(elem.comp().arr[1] == 3.4); // And obviously when initialized directly - auto ex = ExampleWithUserInit{}; + auto ex = MutableExampleWithUserInit{}; REQUIRE(ex.i16Val() == 42); REQUIRE(ex.floats()[0] == 3.14f); REQUIRE(ex.floats()[1] == 1.23f); @@ -505,7 +558,7 @@ TEST_CASE("const correct iterators on const collections", "[const-correctness]") const auto collection = ExampleHitCollection(); // this essentially checks the whole "chain" from begin() / end() through // iterator operators - for (auto hit : collection) { + for (auto hit [[maybe_unused]] : collection) { STATIC_REQUIRE(std::is_same_v); // const collection iterators should only return // immutable objects } @@ -606,7 +659,7 @@ TEST_CASE("Subset collection can handle subsets", "[subset-colls]") { } } -TEST_CASE("Collection iterators work with subset collections", "[LEAK-FAIL][subset-colls]") { +TEST_CASE("Collection iterators work with subset collections", "[subset-colls]") { auto hits = ExampleHitCollection(); auto hit1 = hits.create(0x42ULL, 0., 0., 0., 0.); auto hit2 = hits.create(0x42ULL, 1., 1., 1., 1.); @@ -650,7 +703,7 @@ TEST_CASE("Cannot convert a subset collection into a normal collection", "[subse TEST_CASE("Subset collection only handles tracked objects", "[subset-colls]") { auto clusterRefs = ExampleClusterCollection(); clusterRefs.setSubsetCollection(); - auto cluster = ExampleCluster(); + auto cluster = MutableExampleCluster(); REQUIRE_THROWS_AS(clusterRefs.push_back(cluster), std::invalid_argument); REQUIRE_THROWS_AS(clusterRefs.create(), std::logic_error); diff --git a/tests/write_ascii.cpp b/tests/write_ascii.cpp index b9329db0d..bc9dbe9c6 100644 --- a/tests/write_ascii.cpp +++ b/tests/write_ascii.cpp @@ -58,23 +58,23 @@ int main() { auto item1 = MutableEventInfo(); item1.Number(i); info.push_back(item1); - auto hit1 = ExampleHit(0xbad, 0., 0., 0., 23. + i); - auto hit2 = ExampleHit(0xcaffee, 1., 0., 0., 12. + i); + auto hit1 = MutableExampleHit(0xbad, 0., 0., 0., 23. + i); + auto hit2 = MutableExampleHit(0xcaffee, 1., 0., 0., 12. + i); hits.push_back(hit1); hits.push_back(hit2); // ---- add some MC particles ---- - auto mcp0 = ExampleMC(); - auto mcp1 = ExampleMC(); - auto mcp2 = ExampleMC(); - auto mcp3 = ExampleMC(); - auto mcp4 = ExampleMC(); - auto mcp5 = ExampleMC(); - auto mcp6 = ExampleMC(); - auto mcp7 = ExampleMC(); - auto mcp8 = ExampleMC(); - auto mcp9 = ExampleMC(); + auto mcp0 = MutableExampleMC(); + auto mcp1 = MutableExampleMC(); + auto mcp2 = MutableExampleMC(); + auto mcp3 = MutableExampleMC(); + auto mcp4 = MutableExampleMC(); + auto mcp5 = MutableExampleMC(); + auto mcp6 = MutableExampleMC(); + auto mcp7 = MutableExampleMC(); + auto mcp8 = MutableExampleMC(); + auto mcp9 = MutableExampleMC(); mcps.push_back(mcp0); mcps.push_back(mcp1); @@ -154,7 +154,7 @@ int main() { auto ref = MutableExampleReferencingType(); refs.push_back(ref); - auto ref2 = ExampleReferencingType(); + auto ref2 = MutableExampleReferencingType(); refs2.push_back(ref2); ref.addClusters(cluster); @@ -175,7 +175,7 @@ int main() { oneRels.push_back(oneRel); // write non-filled relation - auto oneRelEmpty = ExampleWithOneRelation(); + auto oneRelEmpty = MutableExampleWithOneRelation(); oneRels.push_back(oneRelEmpty); auto vec = MutableExampleWithVectorMember(); diff --git a/tests/write_frame.h b/tests/write_frame.h index ee4adf0ff..8a7b71619 100644 --- a/tests/write_frame.h +++ b/tests/write_frame.h @@ -120,11 +120,8 @@ auto createMCRefCollection(const ExampleMCCollection& mcps, const ExampleMCColle auto createHitCollection(int i) { ExampleHitCollection hits; - auto hit1 = ExampleHit(0xbad, 0., 0., 0., 23. + i); - auto hit2 = ExampleHit(0xcaffee, 1., 0., 0., 12. + i); - - hits.push_back(hit1); - hits.push_back(hit2); + auto hit1 = hits.create(0xbadULL, 0., 0., 0., 23. + i); + auto hit2 = hits.create(0xcaffeeULL, 1., 0., 0., 12. + i); return hits; } @@ -173,8 +170,7 @@ auto createReferencingCollections(const ExampleClusterCollection& clusters) { auto ref = MutableExampleReferencingType(); refs.push_back(ref); - auto ref2 = ExampleReferencingType(); - refs2.push_back(ref2); + auto ref2 = refs2.create(); ref.addClusters(clusters[2]); ref.addRefs(ref2); @@ -194,8 +190,7 @@ auto createOneRelCollection(const ExampleClusterCollection& clusters) { oneRels.push_back(oneRel); // write non-filled relation - auto oneRelEmpty = ExampleWithOneRelation(); - oneRels.push_back(oneRelEmpty); + oneRels.create(); return oneRels; } diff --git a/tests/write_test.h b/tests/write_test.h index 8556cf9c0..492fb049b 100644 --- a/tests/write_test.h +++ b/tests/write_test.h @@ -92,8 +92,8 @@ void write(podio::EventStore& store, WriterT& writer) { auto& colMD = store.getCollectionMetaData(hits.getID()); colMD.setValue("CellIDEncodingString", "system:8,barrel:3,layer:6,slice:5,x:-16,y:-16"); - auto hit1 = ExampleHit(0xbad, 0., 0., 0., 23. + i); - auto hit2 = ExampleHit(0xcaffee, 1., 0., 0., 12. + i); + auto hit1 = MutableExampleHit(0xbad, 0., 0., 0., 23. + i); + auto hit2 = MutableExampleHit(0xcaffee, 1., 0., 0., 12. + i); hits.push_back(hit1); hits.push_back(hit2); @@ -102,16 +102,16 @@ void write(podio::EventStore& store, WriterT& writer) { hitRefs.push_back(hit1); // ---- add some MC particles ---- - auto mcp0 = ExampleMC(); - auto mcp1 = ExampleMC(); - auto mcp2 = ExampleMC(); - auto mcp3 = ExampleMC(); - auto mcp4 = ExampleMC(); - auto mcp5 = ExampleMC(); - auto mcp6 = ExampleMC(); - auto mcp7 = ExampleMC(); - auto mcp8 = ExampleMC(); - auto mcp9 = ExampleMC(); + auto mcp0 = MutableExampleMC(); + auto mcp1 = MutableExampleMC(); + auto mcp2 = MutableExampleMC(); + auto mcp3 = MutableExampleMC(); + auto mcp4 = MutableExampleMC(); + auto mcp5 = MutableExampleMC(); + auto mcp6 = MutableExampleMC(); + auto mcp7 = MutableExampleMC(); + auto mcp8 = MutableExampleMC(); + auto mcp9 = MutableExampleMC(); mcps.push_back(mcp0); mcps.push_back(mcp1); @@ -227,7 +227,7 @@ void write(podio::EventStore& store, WriterT& writer) { auto ref = MutableExampleReferencingType(); refs.push_back(ref); - auto ref2 = ExampleReferencingType(); + auto ref2 = MutableExampleReferencingType(); refs2.push_back(ref2); ref.addClusters(cluster); @@ -248,7 +248,7 @@ void write(podio::EventStore& store, WriterT& writer) { oneRels.push_back(oneRel); // write non-filled relation - auto oneRelEmpty = ExampleWithOneRelation(); + auto oneRelEmpty = MutableExampleWithOneRelation(); oneRels.push_back(oneRelEmpty); auto vec = MutableExampleWithVectorMember();