File indexing completed on 2026-10-25 09:28:05
0001 #ifndef PODIO_DETAIL_LINKCOLLECTIONIMPL_H
0002 #define PODIO_DETAIL_LINKCOLLECTIONIMPL_H
0003
0004 #include "podio/ObjectID.h"
0005 #include "podio/detail/Link.h"
0006 #include "podio/detail/LinkCollectionData.h"
0007 #include "podio/detail/LinkCollectionIterator.h"
0008 #include "podio/detail/LinkFwd.h"
0009 #include "podio/detail/LinkObj.h"
0010
0011 #include "podio/CollectionBase.h"
0012 #include "podio/CollectionBufferFactory.h"
0013 #include "podio/CollectionBuffers.h"
0014 #include "podio/DatamodelRegistry.h"
0015 #include "podio/ICollectionProvider.h"
0016 #include "podio/SchemaEvolution.h"
0017 #include "podio/detail/Pythonizations.h"
0018 #include "podio/utilities/MaybeSharedPtr.h"
0019 #include "podio/utilities/StaticConcatenate.h"
0020 #include "podio/utilities/TypeHelpers.h"
0021
0022 #include <algorithm>
0023 #include <iterator>
0024 #include <ranges>
0025 #include <string_view>
0026
0027 #ifdef PODIO_JSON_OUTPUT
0028 #include "nlohmann/json.hpp"
0029 #endif
0030
0031 #include <iomanip>
0032 #include <memory>
0033 #include <mutex>
0034 #include <ostream>
0035 #include <string>
0036 #include <type_traits>
0037 #include <utility>
0038
0039 namespace podio {
0040
0041 template <typename FromT, typename ToT>
0042 class LinkCollection : public podio::CollectionBase {
0043 static_assert(std::is_same_v<FromT, detail::GetDefaultHandleType<FromT>>,
0044 "Links need to be instantiated with the default types!");
0045 static_assert(std::is_same_v<ToT, detail::GetDefaultHandleType<ToT>>,
0046 "Links need to be instantiated with the default types!");
0047
0048
0049 using CollectionDataT = podio::LinkCollectionData<FromT, ToT>;
0050
0051 public:
0052 using from_type = FromT;
0053 using to_type = ToT;
0054 using value_type = Link<FromT, ToT>;
0055 using mutable_type = MutableLink<FromT, ToT>;
0056 using const_iterator = LinkCollectionIterator<FromT, ToT>;
0057 using iterator = LinkMutableCollectionIterator<FromT, ToT>;
0058 using difference_type = ptrdiff_t;
0059 using size_type = size_t;
0060 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
0061 using reverse_iterator = std::reverse_iterator<iterator>;
0062
0063 LinkCollection() = default;
0064
0065 LinkCollection(CollectionDataT&& data, bool isSubsetColl) :
0066 m_isSubsetColl(isSubsetColl), m_collectionID(0), m_storage(std::move(data)) {
0067 }
0068
0069
0070 LinkCollection(const LinkCollection&) = delete;
0071 LinkCollection& operator=(const LinkCollection&) = delete;
0072 LinkCollection(LinkCollection&&) = default;
0073 LinkCollection& operator=(LinkCollection&&) = default;
0074 #if defined(__cpp_lib_containers_ranges)
0075
0076
0077 template <detail::RangeConvertibleTo<value_type> R>
0078 LinkCollection(std::from_range_t, R&& range) : LinkCollection() {
0079 if constexpr (detail::RangeOf<R, value_type>) {
0080 setSubsetCollection();
0081 }
0082
0083 for (auto&& elem : range) {
0084 push_back(std::forward<decltype(elem)>(elem));
0085 }
0086 }
0087 #endif
0088
0089 ~LinkCollection() override {
0090
0091 m_storage.clear(m_isSubsetColl);
0092 }
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109 template <detail::RangeConvertibleTo<value_type> R>
0110 static LinkCollection from(R&& range) {
0111 LinkCollection coll;
0112 if constexpr (detail::RangeOf<R, value_type>) {
0113 coll.setSubsetCollection();
0114 }
0115
0116
0117
0118
0119
0120
0121 for (auto&& elem : range) {
0122 coll.push_back(std::forward<decltype(elem)>(elem));
0123 }
0124
0125 return coll;
0126 }
0127
0128
0129 mutable_type create() {
0130 if (m_isSubsetColl) {
0131 throw std::logic_error("Cannot create new elements on a subset collection");
0132 }
0133
0134 auto obj = m_storage.entries.emplace_back(new LinkObj<FromT, ToT>());
0135 obj->id = {int(m_storage.entries.size() - 1), m_collectionID};
0136 return mutable_type(podio::utils::MaybeSharedPtr(obj));
0137 }
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158 template <typename FromU, typename ToU>
0159 requires detail::LinkInitializableFrom<FromT, ToT, FromU, ToU> ||
0160 detail::LinkInitializableFrom<ToT, FromT, FromU, ToU>
0161 mutable_type create(FromU&& from, ToU&& to, float weight = 1.0f) {
0162 auto link = create();
0163 link.setWeight(weight);
0164
0165 if constexpr (detail::LinkInitializableFrom<FromT, ToT, FromU, ToU>) {
0166 link.setFrom(std::forward<FromU>(from));
0167 link.setTo(std::forward<ToU>(to));
0168 } else {
0169 link.setTo(std::forward<FromU>(from));
0170 link.setFrom(std::forward<ToU>(to));
0171 }
0172 return link;
0173 }
0174
0175
0176 value_type operator[](std::size_t index) const {
0177 return value_type(m_storage.entries[index]);
0178 }
0179
0180 mutable_type operator[](std::size_t index) {
0181 return mutable_type(podio::utils::MaybeSharedPtr(m_storage.entries[index]));
0182 }
0183
0184 value_type at(std::size_t index) const {
0185 return value_type(m_storage.entries.at(index));
0186 }
0187
0188 mutable_type at(std::size_t index) {
0189 return mutable_type(podio::utils::MaybeSharedPtr(m_storage.entries.at(index)));
0190 }
0191
0192 void push_back(mutable_type object) {
0193
0194
0195
0196
0197 if (!m_isSubsetColl) {
0198 auto obj = object.m_obj;
0199 if (obj->id.index == podio::ObjectID::untracked) {
0200 obj->id = {static_cast<int>(m_storage.entries.size()), m_collectionID};
0201 m_storage.entries.push_back(obj.release());
0202 } else {
0203 throw std::invalid_argument("Object already in a collection. Cannot add it to a second collection");
0204 }
0205
0206 } else {
0207 push_back(value_type(object));
0208 }
0209 }
0210
0211 void push_back(value_type object) {
0212 if (!m_isSubsetColl) {
0213 throw std::invalid_argument("Can only add immutable objects to subset collections");
0214 }
0215 auto obj = object.m_obj;
0216 if (obj->id.index < 0) {
0217 throw std::invalid_argument(
0218 "Object needs to be tracked by another collection in order for it to be storable in a subset collection");
0219 }
0220 m_storage.entries.push_back(obj.release());
0221 }
0222
0223
0224 size_t size() const override {
0225 return m_storage.entries.size();
0226 }
0227
0228
0229 std::size_t max_size() const override {
0230 const auto maxStorage = m_storage.entries.max_size();
0231 if (!m_isSubsetColl) {
0232
0233 const auto maxIndex = std::numeric_limits<decltype(ObjectID::index)>::max();
0234 return std::min<size_t>(maxIndex, maxStorage);
0235 }
0236 return maxStorage;
0237 }
0238
0239
0240 bool empty() const override {
0241 return m_storage.entries.empty();
0242 }
0243
0244 void clear() override {
0245 m_storage.clear(m_isSubsetColl);
0246 m_isPrepared = false;
0247 }
0248
0249 void print(std::ostream& os = std::cout, bool flush = true) const override {
0250 os << *this;
0251 if (flush) {
0252 os.flush();
0253 }
0254 }
0255
0256
0257 const_iterator begin() const {
0258 return const_iterator(0, &m_storage.entries);
0259 }
0260 const_iterator cbegin() const {
0261 return begin();
0262 }
0263 const_iterator end() const {
0264 return const_iterator(m_storage.entries.size(), &m_storage.entries);
0265 }
0266 const_iterator cend() const {
0267 return end();
0268 }
0269 iterator begin() {
0270 return iterator(0, &m_storage.entries);
0271 }
0272 iterator end() {
0273 return iterator(m_storage.entries.size(), &m_storage.entries);
0274 }
0275
0276 reverse_iterator rbegin() {
0277 return reverse_iterator(end());
0278 }
0279 const_reverse_iterator rbegin() const {
0280 return const_reverse_iterator(end());
0281 }
0282 const_reverse_iterator crbegin() const {
0283 return rbegin();
0284 }
0285 reverse_iterator rend() {
0286 return reverse_iterator(begin());
0287 }
0288 const_reverse_iterator rend() const {
0289 return const_reverse_iterator(begin());
0290 }
0291 const_reverse_iterator crend() const {
0292 return rend();
0293 }
0294
0295
0296 bool hasID() const override {
0297 return getID() != static_cast<uint32_t>(podio::ObjectID::untracked) &&
0298 getID() != static_cast<uint32_t>(podio::ObjectID::invalid);
0299 }
0300
0301 [[deprecated("isValid will be removed, use hasID() if you want to check if it has an ID, otherwise assume the "
0302 "collection is valid")]]
0303 bool isValid() const override {
0304 return hasID();
0305 }
0306
0307 podio::CollectionWriteBuffers getBuffers() override {
0308 return m_storage.getCollectionBuffers(m_isSubsetColl);
0309 }
0310
0311 constexpr static std::string_view typeName =
0312 podio::utils::static_concatenate_v<podio::detail::link_coll_name_prefix, FromT::typeName,
0313 podio::detail::link_name_infix, ToT::typeName,
0314 podio::detail::link_name_suffix>;
0315
0316 constexpr static std::string_view valueTypeName = value_type::typeName;
0317 constexpr static std::string_view dataTypeName = "podio::LinkData";
0318
0319 const std::string_view getTypeName() const override {
0320 return typeName;
0321 }
0322
0323 const std::string_view getValueTypeName() const override {
0324 return valueTypeName;
0325 }
0326
0327 const std::string_view getDataTypeName() const override {
0328 return dataTypeName;
0329 }
0330
0331
0332 static void __cppyy_pythonize__(PyObject* klass, const std::string& name) {
0333 podio::detail::pythonizations::pythonize_subscript(klass, name);
0334 }
0335
0336 bool isSubsetCollection() const override {
0337 return m_isSubsetColl;
0338 }
0339
0340 void setSubsetCollection(bool setSubset = true) override {
0341 if (m_isSubsetColl != setSubset && !m_storage.entries.empty()) {
0342 throw std::logic_error("Cannot change the character of a collection that already contains elements");
0343 }
0344
0345 if (setSubset) {
0346 m_storage.makeSubsetCollection();
0347 }
0348 m_isSubsetColl = setSubset;
0349 }
0350
0351 void setID(uint32_t id) override {
0352 m_collectionID = id;
0353 if (!m_isSubsetColl) {
0354 std::ranges::for_each(m_storage.entries, [id](auto* obj) { obj->id = {obj->id.index, id}; });
0355 }
0356 }
0357
0358 uint32_t getID() const override {
0359 return m_collectionID;
0360 }
0361
0362 void prepareForWrite() const override {
0363 std::lock_guard lock{*m_storageMtx};
0364
0365
0366 if (m_isPrepared) {
0367 return;
0368 }
0369 m_storage.prepareForWrite(m_isSubsetColl);
0370 m_isPrepared = true;
0371 }
0372
0373 void prepareAfterRead() override {
0374
0375 if (m_isPrepared) {
0376 return;
0377 }
0378
0379 if (!m_isSubsetColl) {
0380
0381 m_storage.prepareAfterRead(m_collectionID);
0382 }
0383
0384
0385 m_isPrepared = true;
0386 }
0387
0388 bool setReferences(const ICollectionProvider* collectionProvider) override {
0389 return m_storage.setReferences(collectionProvider, m_isSubsetColl);
0390 }
0391
0392 static constexpr SchemaVersionT schemaVersion = 1;
0393
0394 SchemaVersionT getSchemaVersion() const override {
0395 return schemaVersion;
0396 }
0397
0398 size_t getDatamodelRegistryIndex() const override {
0399 return podio::DatamodelRegistry::NoDefinitionNecessary;
0400 }
0401
0402 private:
0403
0404
0405
0406 friend CollectionDataT;
0407
0408 mutable bool m_isPrepared{false};
0409 bool m_isSubsetColl{false};
0410 uint32_t m_collectionID{0};
0411 mutable std::unique_ptr<std::mutex> m_storageMtx{std::make_unique<std::mutex>()};
0412 mutable CollectionDataT m_storage{};
0413 };
0414
0415 template <typename FromT, typename ToT>
0416 std::ostream& operator<<(std::ostream& o, const LinkCollection<FromT, ToT>& v) {
0417 const auto old_flags = o.flags();
0418 o << " id: weight:" << '\n';
0419 for (const auto&& el : v) {
0420 o << std::scientific << std::showpos << std::setw(12) << el.id() << " " << std::setw(12) << " " << el.getWeight()
0421 << '\n';
0422
0423 o << " from : ";
0424 o << el.getFrom().id() << std::endl;
0425 o << " to : ";
0426 o << el.getTo().id() << std::endl;
0427 }
0428
0429 o.flags(old_flags);
0430 return o;
0431 }
0432
0433 namespace detail {
0434 template <typename FromT, typename ToT>
0435 podio::CollectionReadBuffers createLinkBuffers(bool subsetColl) {
0436 auto readBuffers = podio::CollectionReadBuffers{};
0437 readBuffers.type = podio::LinkCollection<FromT, ToT>::typeName;
0438 readBuffers.schemaVersion = podio::LinkCollection<FromT, ToT>::schemaVersion;
0439 readBuffers.data = subsetColl ? nullptr : new LinkDataContainer();
0440
0441
0442 const auto nRefs = subsetColl ? 1 : 2;
0443 readBuffers.references = new podio::CollRefCollection(nRefs);
0444 for (auto& ref : *readBuffers.references) {
0445
0446 ref = std::make_unique<std::vector<podio::ObjectID>>();
0447 }
0448
0449 readBuffers.createCollection = [](podio::CollectionReadBuffers&& buffers, bool isSubsetColl) {
0450 LinkCollectionData<FromT, ToT> data(std::move(buffers), isSubsetColl);
0451 return std::make_unique<LinkCollection<FromT, ToT>>(std::move(data), isSubsetColl);
0452 };
0453
0454 readBuffers.deleteBuffers = [](podio::CollectionReadBuffers& buffers) {
0455 if (buffers.data) {
0456
0457
0458
0459 delete static_cast<LinkDataContainer*>(buffers.data);
0460 buffers.data = nullptr;
0461 }
0462 delete buffers.references;
0463 buffers.references = nullptr;
0464 delete buffers.vectorMembers;
0465 buffers.vectorMembers = nullptr;
0466 };
0467
0468 return readBuffers;
0469 }
0470
0471 template <typename FromT, typename ToT>
0472 bool registerLinkCollection(const std::string_view linkTypeName) {
0473 const static auto reg = [&linkTypeName]() {
0474 const auto schemaVersion = LinkCollection<FromT, ToT>::schemaVersion;
0475
0476 auto& factory = CollectionBufferFactory::mutInstance();
0477 factory.registerCreationFunc(std::string(linkTypeName), schemaVersion, createLinkBuffers<FromT, ToT>);
0478
0479
0480
0481 podio::SchemaEvolution::mutInstance().registerEvolutionFunc(std::string(linkTypeName), schemaVersion,
0482 schemaVersion, SchemaEvolution::noOpSchemaEvolution,
0483 SchemaEvolution::Priority::AutoGenerated);
0484
0485 return true;
0486 }();
0487 return reg;
0488 }
0489 }
0490
0491 #if defined(PODIO_JSON_OUTPUT) && !defined(__CLING__)
0492 template <typename FromT, typename ToT>
0493 void to_json(nlohmann::json& j, const podio::LinkCollection<FromT, ToT>& collection) {
0494 j = nlohmann::json::array();
0495 for (auto&& elem : collection) {
0496 j.emplace_back(elem);
0497 }
0498 }
0499 #endif
0500
0501 }
0502
0503 #endif