Skip to content

Commit

Permalink
Rename Association to Link after EDM4hep discussion
Browse files Browse the repository at this point in the history
  • Loading branch information
tmadlener committed Jul 31, 2024
1 parent 82faccc commit fcddf16
Show file tree
Hide file tree
Showing 25 changed files with 1,057 additions and 1,060 deletions.
102 changes: 51 additions & 51 deletions doc/associations.md → doc/links.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,65 @@
# Associating unrelated objects with each other
Sometimes it is necessary to build associations between objects whose datatypes are
Sometimes it is necessary to build links between objects whose datatypes are
not related via a `OneToOneRelation` or a `OneToManyRelation`. These *external
relations* are called *Associations* in podio, and they are implemented as a
relations* are called *Links* in podio, and they are implemented as a
templated version of the code that would be generated by the following yaml
snippet (in this case between generic `FromT` and `ToT` datatypes):

```yaml
Association:
Description: "A weighted association between a FromT and a ToT"
Link:
Description: "A weighted link between a FromT and a ToT"
Author: "P. O. Dio"
Members:
- float weight // the weight of the association
- float weight // the weight of the link
OneToOneRelations:
- FromT from // reference to the FromT
- ToT to // reference to the ToT
```
## `Association` basics
`Association`s are implemented as templated classes forming a similar structure
## `Link` basics
`Link`s are implemented as templated classes forming a similar structure
as other podio generated classes, with several layers of which users only ever
interact with the *User layer*. This layer has the following basic classes
```cpp
/// The collection class that forms the basis of I/O and also is the main entry point
template<typename FromT, typename ToT>
class AssociationCollection;
class LinkCollection;
/// The default (immutable) class that one gets after reading a collection
template<typename FromT, typename ToT>
class Association;
class Link;
/// The mutable class for creating associations before writing them
/// The mutable class for creating links before writing them
template<typename FromT, typename ToT>
class MutableAssociation;
class MutableLink;
```

Although the names of the template parameters, `FromT` and `ToT` imply a
direction of the association, from a technical point of view nothing actually
direction of the link, from a technical point of view nothing actually
enforces this direction, unless `FromT` and `ToT` are both of the same type.
Hence, associations can effectively be treated as bi-directional, and one
Hence, links can effectively be treated as bi-directional, and one
combination of `FromT` and `ToT` should be enough for all use cases (see also
the [usage section](#how-to-use-associations)).
the [usage section](#how-to-use-links)).

For a more detailed explanation of the internals and the actual implementation
see [the implementation details](#implementation-details).

## How to use `Association`s
Using `Association`s is quite simple. In line with other datatypes that are
## How to use `Link`s
Using `Link`s is quite simple. In line with other datatypes that are
generated by podio all the functionality can be gained by including the
corresponding `Collection` header. After that it is generally recommended to
introduce a type alias for easier usage. **As a general rule `Associations` need
introduce a type alias for easier usage. **As a general rule `Links` need
to be declared with the default (immutable) types.** Trying to instantiate them
with `Mutable` types will result in a compilation error.

```cpp
#include "podio/AssociationCollection.h"
#include "podio/LinkCollection.h"
#include "edm4hep/MCParticleCollection.h"
#include "edm4hep/ReconstructedParticleCollection.h"
// declare a new association type
using MCRecoParticleAssociationCollection = podio::AssociationCollection<edm4hep::MCParticle,
// declare a new link type
using MCRecoParticleLinkCollection = podio::LinkCollection<edm4hep::MCParticle,
edm4hep::ReconstructedParticle>;
```

Expand All @@ -68,8 +68,8 @@ This can now be used exactly as any other podio generated collection, i.e.
edm4hep::MCParticle mcParticle{};
edm4hep::ReconstructedParticle recoParticle{};
auto mcRecoAssocs = MCRecoParticleAssociationCollection{};
auto assoc = mcRecoAssocs.create(); // create an association;
auto mcRecoAssocs = MCRecoParticleLinkCollection{};
auto assoc = mcRecoAssocs.create(); // create an link;
assoc.setFrom(mcParticle);
assoc.setTo(recoParticle);
assoc.setWeight(1.0); // This is also the default value!
Expand All @@ -95,7 +95,7 @@ auto recoP = assoc.get<edm4hep::ReconstructedParticle>();
auto weight = assoc.getWeight();
```

It is also possible to access the elements of an association via an index based
It is also possible to access the elements of an link via an index based
`get` (similar to `std::tuple`). In this case `0` corresponds to `getFrom`, `1`
corresponds to `getTo` and `2` corresponds to the weight. The main purpose of
this feature is to enable structured bindings:
Expand All @@ -105,29 +105,29 @@ const auto& [mcP, recoP, weight] = assoc;
```

The above three examples are three equivalent ways of retrieving the same things
from an `Association`. **The templated `get` and `set` methods are only available
from an `Link`. **The templated `get` and `set` methods are only available
if `FromT` and `ToT` are not the same type** and will lead to a compilation
error otherwise.

### Enabling I/O capabilities for `Association`s
### Enabling I/O capabilities for `Link`s

`Association`s do not have I/O support out of the box. This has to be enabled via
the `PODIO_DECLARE_ASSOCIATION` macro (defined in the `AssociationCollection.h`
header). If you simply want to be able to read / write `Association`s in a
`Link`s do not have I/O support out of the box. This has to be enabled via
the `PODIO_DECLARE_LINK` macro (defined in the `LinkCollection.h`
header). If you simply want to be able to read / write `Link`s in a
standalone executable, it is enough to use this macro somewhere in the
executable, e.g. to enable I/O capabilities for the `MCRecoParticleAssociation`s
executable, e.g. to enable I/O capabilities for the `MCRecoParticleLink`s
used above this would look like:

```cpp
PODIO_DECLARE_ASSOCIATION(edm4hep::MCParticle, edm4hep::ReconstructedParticle)
PODIO_DECLARE_LINK(edm4hep::MCParticle, edm4hep::ReconstructedParticle)
```

The macro will also enable SIO support if the `PODIO_ENABLE_SIO=1` is passed to
the compiler. This is done by default when linking against the
`podio::podioSioIO` library in CMake.

For enabling I/O support for shared datamodel libraries, it is necessary to have
all the necessary combinations of types declared via `PODIO_DECLARE_ASSOCIATION`
all the necessary combinations of types declared via `PODIO_DECLARE_LINK`
and have that compiled into the library. This is necessary if you want to use
the python bindings, since they rely on dynamically loading the datamodel
libraries.
Expand All @@ -146,54 +146,54 @@ be obtained by generating them via the yaml snippet above and sprinkling some

### File structure

The user facing `"podio/AssociationCollection.h"` header essentially just
defines the `PODIO_DECLARE_ASSOCIATION` macro (depending on whether SIO support
The user facing `"podio/LinkCollection.h"` header essentially just
defines the `PODIO_DECLARE_LINK` macro (depending on whether SIO support
is desired and possible or not). All the actual implementation is done in the
following files:

- [`"podio/detail/AssociationCollectionImpl.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/AssociationCollectionImpl.h):
- [`"podio/detail/LinkCollectionImpl.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/LinkCollectionImpl.h):
for the collection functionality
- [`"podio/detail/Association.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/Association.h):
for the functionality of single association
- [`"podio/detail/AssociationCollectionIterator.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/AssociationCollectionIterator.h):
- [`"podio/detail/Link.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/Link.h):
for the functionality of single link
- [`"podio/detail/LinkCollectionIterator.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/LinkCollectionIterator.h):
for the collection iterator functionality
- [`"podio/detail/AssociationObj.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/AssociationObj.h):
- [`"podio/detail/LinkObj.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/LinkObj.h):
for the object layer functionality
- [`"podio/detail/AssociationCollectionData.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/AssociationCollectionData.h):
- [`"podio/detail/LinkCollectionData.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/LinkCollectionData.h):
for the collection data functionality
- [`"podio/detail/AssociationFwd.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/AssociationFwd.h):
- [`"podio/detail/LinkFwd.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/LinkFwd.h):
for some type helper functionality and some forward declarations that are used
throughout the other headers
- [`"podio/detail/AssociationSIOBlock.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/AssociationSIOBlock.h):
- [`"podio/detail/LinkSIOBlock.h"`](https://github.com/AIDASoft/podio/blob/master/include/podio/detail/LinkSIOBlock.h):
for defining the SIOBlocks that are necessary to use SIO

As is visible from this structure, we did not introduce an `AssociationData`
As is visible from this structure, we did not introduce an `LinkData`
class, since that would effectively just be a `float` wrapped inside a `struct`.

### Default and `Mutable` `Association` classes
### Default and `Mutable` `Link` classes

A quick look into the `AssociationFwd.h` header will reveal that the default and
`Mutable` `Association` classes are in fact just partial specialization of the
`AssociationT` class that takes a `bool Mutable` as third template argument. The
A quick look into the `LinkFwd.h` header will reveal that the default and
`Mutable` `Link` classes are in fact just partial specialization of the
`LinkT` class that takes a `bool Mutable` as third template argument. The
same approach is also followed by the `AssocationCollectionIterator`s:

```cpp
template<typename FromT, typename ToT, bool Mutable>
class AssociationT;
class LinkT;
template <typename FromT, typename ToT>
using Association = AssociationT<FromT, ToT, false>;
using Link = LinkT<FromT, ToT, false>;
template <typename FromT, typename ToT>
using MutableAssociation = AssociationT<FromT, ToT, true>;
using MutableLink = LinkT<FromT, ToT, true>;
```

Throughout the implementation it is assumed that `FromT` and `ToT` always are the
default handle types. This is ensured through `static_assert`s in the
`AssociationCollection` to make sure it can only be instantiated with those. The
`LinkCollection` to make sure it can only be instantiated with those. The
`GetDefaultHandleType` helper templates are used to retrieve the correct type
from any `FromT` regardless of whether it is a mutable or a default handle type
With this in mind, effectively all mutating operations on `Association`s are
With this in mind, effectively all mutating operations on `Link`s are
defined using [*SFINAE*](https://en.cppreference.com/w/cpp/language/sfinae)
using the following template structure (taking here `setFrom` as an example)

Expand Down
37 changes: 0 additions & 37 deletions include/podio/AssociationCollection.h

This file was deleted.

35 changes: 35 additions & 0 deletions include/podio/LinkCollection.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef PODIO_LINKCOLLECTION_H
#define PODIO_LINKCOLLECTION_H

#include "podio/detail/LinkCollectionImpl.h"

// Preprocessor helper macros for concatenating tokens at preprocessing times
// Necessary because we use __COUNTER__ below for unique names of static
// variables for values returned by registration function calls
#define PODIO_PP_CONCAT_IMPL(x, y) x##y
#define PODIO_PP_CONCAT(x, y) PODIO_PP_CONCAT_IMPL(x, y)

#ifndef PODIO_ENABLE_SIO
#define PODIO_ENABLE_SIO 0
#endif

#if PODIO_ENABLE_SIO && __has_include("podio/detail/LinkSIOBlock.h")
#include "podio/detail/LinkSIOBlock.h"
/// Main macro for declaring links. Takes care of the following things:
/// - Registering the necessary buffer creation functionality with the
/// CollectionBufferFactory.
/// - Registering the necessary SIOBlock with the SIOBlock factory
#define PODIO_DECLARE_LINK(FromT, ToT) \
const static auto PODIO_PP_CONCAT(REGISTERED_LINK_, __COUNTER__) = \
podio::detail::registerLinkCollection<FromT, ToT>(podio::detail::linkCollTypeName<FromT, ToT>()); \
const static auto PODIO_PP_CONCAT(LINK_SIO_BLOCK_, __COUNTER__) = podio::LinkSIOBlock<FromT, ToT>{};
#else
/// Main macro for declaring links. Takes care of the following things:
/// - Registering the necessary buffer creation functionality with the
/// CollectionBufferFactory.
#define PODIO_DECLARE_LINK(FromT, ToT) \
const static auto PODIO_PP_CONCAT(REGISTERED_LINK_, __COUNTER__) = \
podio::detail::registerLinkCollection<FromT, ToT>(podio::detail::linkCollTypeName<FromT, ToT>());
#endif

#endif // PODIO_LINKCOLLECTION_H
Loading

0 comments on commit fcddf16

Please sign in to comment.