Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions include/podio/Frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,29 @@ class Frame {
/// if it is not
const podio::CollectionBase* get(const std::string& name) const;

/// Get the collection to which the passed object belongs from the Frame.
///
/// @tparam CollT The type of the desired collection
/// @param object The object for which the collection it belongs to should
/// be retrieved
///
/// @returns A const reference to the collection if it is available or to
/// an empty (static) collection
template <CollectionType CollT>
const CollT& get(const typename CollT::value_type& object) const;

/// Get the collection pointer to which the passed object belongs from the
/// Frame.
///
/// @tparam O The type of the object
/// @param object The object for which the collection it belongs to should
/// be retrieved
///
/// @returns A const pointer to a collection if it is available or a nullptr
/// if it is not
template <ObjectType O>
inline const typename O::collection_type* get(const O& object) const;

/// (Destructively) move a collection into the Frame and get a reference to
/// the inserted collection back for further use.
///
Expand Down Expand Up @@ -382,10 +405,22 @@ const CollT& Frame::get(const std::string& name) const {
return emptyColl;
}

template <CollectionType CollT>
const CollT& Frame::get(const typename CollT::value_type& object) const {
const auto name = m_self->getIDTable().name(object.id().collectionID);
return get<CollT>(name.value_or(""));
}

inline const podio::CollectionBase* Frame::get(const std::string& name) const {
return m_self->get(name);
}

template <ObjectType O>
inline const typename O::collection_type* Frame::get(const O& object) const {
const auto name = m_self->getIDTable().name(object.id().collectionID);
return dynamic_cast<const typename O::collection_type*>(get(name.value_or("")));
}

inline void Frame::put(std::unique_ptr<podio::CollectionBase> coll, const std::string& name) {
const auto* retColl = m_self->put(std::move(coll), name);
if (!retColl) {
Expand Down
2 changes: 1 addition & 1 deletion include/podio/detail/Link.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class LinkT {

public:
using mutable_type = podio::MutableLink<FromT, ToT>;
using value_type = podio::Link<FromT, ToT>;
using object_type = podio::Link<FromT, ToT>;
using collection_type = podio::LinkCollection<FromT, ToT>;

static constexpr std::string_view typeName =
Expand Down
14 changes: 13 additions & 1 deletion include/podio/utilities/TypeHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ namespace detail {
template <typename T>
using hasObject_t = typename T::object_type;

/// Detector for checking the existence of a value_type member. Necessary to
/// avoid false positives for default handle types from collections, since
/// collections also specify a mutable_type member.
template <typename T>
using hasValue_t = typename T::value_type;

/// Variable template for determining whether type T is a podio generated
/// mutable handle class
template <typename T>
Expand All @@ -184,7 +190,8 @@ namespace detail {
/// Variable template for determining whether type T is a podio generated
/// default handle class
template <typename T>
constexpr static bool isDefaultHandleType = det::is_detected_v<hasMutable_t, std::remove_reference_t<T>>;
constexpr static bool isDefaultHandleType = det::is_detected_v<hasMutable_t, std::remove_reference_t<T>> &&
!det::is_detected_v<hasValue_t, std::remove_reference_t<T>>;

/// Variable template for obtaining the default handle type from any podio
/// generated handle type.
Expand Down Expand Up @@ -245,6 +252,11 @@ namespace detail {
// forward declaration to be able to use it below
class CollectionBase;

/// Concept for checking whether a passed type T is (or can be) a podio
/// generated handle class
template <typename T>
concept ObjectType = detail::isMutableHandleType<T> || detail::isDefaultHandleType<T>;

/// Concept for checking whether a passed type T is a collection
template <typename T>
concept CollectionType = !std::is_abstract_v<T> && std::derived_from<T, CollectionBase> &&
Expand Down
14 changes: 14 additions & 0 deletions python/podio/test_Frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,20 @@ def test_frame_empty_parameters(self):
vals = frame.get_parameter("empty_param")
self.assertEqual(len(vals), 0)

def test_frame_get_collection_from_object(self):
"""Check that using an object (handle) to get a collection works"""
frame = Frame()
hits = ExampleHitCollection()
hit = hits.create()

with self.assertRaises(ReferenceError):
invalidHits = frame.get(hit)
_ = invalidHits[0]

hits = frame.put(hits, "hits")
hitsFromHit = frame.get(hit)
self.assertEqual(hit, hitsFromHit[0])


class FrameReadTest(unittest.TestCase):
"""Unit tests for the Frame python bindings for Frames read from file.
Expand Down
21 changes: 21 additions & 0 deletions tests/unittests/frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,27 @@ TEST_CASE("Frame getName", "[frame][basics]") {
REQUIRE_FALSE(frame.getName(0xfffffff).has_value());
}

TEST_CASE("Frame get from object", "[frame][basics]") {
auto event = podio::Frame{};

auto clusters = ExampleClusterCollection{};
auto cluster = clusters.create(3.14f);

REQUIRE(event.get(cluster) == nullptr);

const auto& invalidClusters = event.get<ExampleClusterCollection>(cluster);
REQUIRE_FALSE(invalidClusters.isValid());

const auto& frameClusters = event.put(std::move(clusters), "clusters");
const auto& objectClusters = event.get<ExampleClusterCollection>(cluster);
REQUIRE(frameClusters[0] == objectClusters[0]);

const auto* collPtr = event.get(cluster);
STATIC_REQUIRE(std::is_same_v<decltype(collPtr), const ExampleClusterCollection*>);
REQUIRE(collPtr != nullptr);
REQUIRE((*collPtr)[0] == cluster);
}

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
Expand Down
6 changes: 5 additions & 1 deletion tests/unittests/links.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,12 @@ TEST_CASE("Links templated accessors", "[links]") {
}
}
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
TEST_CASE("LinkCollection collection concept", "[links][concepts]") {

TEST_CASE("Link concept checks", "[links][concepts]") {
STATIC_REQUIRE(podio::CollectionType<TestLColl>);
STATIC_REQUIRE(podio::ObjectType<TestL>);
STATIC_REQUIRE(podio::ObjectType<TestMutL>);
STATIC_REQUIRE_FALSE(podio::ObjectType<TestLColl>);
}

TEST_CASE("LinkCollection constness", "[links][static-checks][const-correctness]") {
Expand Down
6 changes: 6 additions & 0 deletions tests/unittests/unittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include "extension_model/extension_model.h"

#include "podio/UserDataCollection.h"
#include "podio/utilities/TypeHelpers.h"

TEST_CASE("AutoDelete", "[basics][memory-management]") {
auto coll = EventInfoCollection();
Expand Down Expand Up @@ -126,6 +127,11 @@ TEST_CASE("Component", "[basics]") {
REQUIRE(3 == info.component().data.x);
}

TEST_CASE("ObjectType concept", "[concepts]") {
STATIC_REQUIRE(podio::ObjectType<ExampleHit>);
STATIC_REQUIRE_FALSE(podio::ObjectType<ExampleHitCollection>);
}

TEST_CASE("makeEmpty", "[basics]") {
auto hit = ExampleHit::makeEmpty();
// Any access to such a handle is a crash
Expand Down