diff --git a/doc/collections_as_container.md b/doc/collections_as_container.md index 082214dd4..bb5014139 100644 --- a/doc/collections_as_container.md +++ b/doc/collections_as_container.md @@ -2,7 +2,14 @@ Comparison of the PODIO `Collection`s with a C++ named requirement [*Container*](https://en.cppreference.com/w/cpp/named_req/Container). -The PODIO `Collection`s interface was designed to remind the standard *Container* interface, in particular `std::vector`. The perfect compliance with the *Container* is not achieved as the `Collection`s are concerned with additional semantics such as mutable/immutable element access, associations and relations, and IO which are not part of *Container*. +The PODIO `Collection`s interface was designed to mimic the standard *Container* interface, in particular `std::vector`. Perfect compliance with the *Container* is not achieved as the `Collection`s are concerned with additional semantics such as mutable/immutable element access, associations and relations, and IO which that are not part of *Container*. + +On the implementation level most of the differences with respect to the *Container* comes from the fact that in order to satisfy the additional semantics a `Collection` doesn't directly store [user layer objects](design.md#the-user-layer). Instead, [data layer objects](design.md#the-internal-data-layer) are stored and user layer objects are constructed and returned when needed. Similarly, the `Collection` iterators operate on the user layer objects but don't expose `Collection`'s storage directly to the users. Instead, they construct and return user layer objects when needed. +In other words, a `Collection` utilizes the user layer type as a reference type instead of using plain references (`&` or `&&`) to stored data layer types. + +As a consequence some of the **standard algorithms may not work** with PODIO `Collection` iterators. See [standard algorithm documentation](#collection-and-standard-algorithms) below. + +The following tables list the compliance of a PODIO generated collection with the *Container* named requirement, stating which member types, interfaces, or concepts are fulfilled and which are not. Additionally, there are some comments explaining missing parts or pointing out differences in behaviour. ### Container Types @@ -11,8 +18,8 @@ The PODIO `Collection`s interface was designed to remind the standard *Container | `value_type` | `T` | *[Erasable](https://en.cppreference.com/w/cpp/named_req/Erasable)* | ✔️ yes | Defined as immutable component type | | `reference` | `T&` | | ❌ no | Not defined | | `const_reference` | `const T&` | | ❌ no | Not defined | -| `iterator` | Iterator whose `value_type` is `T` | [*LegacyForwardIterator*](https://en.cppreference.com/w/cpp/named_req/ForwardIterator) convertible to `const_iterator` | ❌ no | `iterator::value_type` not defined, not [*LegacyForwardIterator*](https://en.cppreference.com/w/cpp/named_req/ForwardIterator) ([see below](#legacyforwarditerator)), not convertible to `const_iterator`| -| `const_iterator` | Constant iterator whose `value_type` is `T` | [*LegacyForwardIterator*](https://en.cppreference.com/w/cpp/named_req/ForwardIterator) | ❌ no | `const_iterator::value_type` not defined, not [*LegacyForwardIterator*](https://en.cppreference.com/w/cpp/named_req/ForwardIterator) ([see below](#legacyforwarditerator)) +| `iterator` | Iterator whose `value_type` is `T` | [*LegacyForwardIterator*](https://en.cppreference.com/w/cpp/named_req/ForwardIterator) convertible to `const_iterator` | ❌ no | Defined as podio `MutableCollectionIterator`. `iterator::value_type` not defined, not [*LegacyForwardIterator*](https://en.cppreference.com/w/cpp/named_req/ForwardIterator) ([see below](#legacyforwarditerator)), not convertible to `const_iterator`| +| `const_iterator` | Constant iterator whose `value_type` is `T` | [*LegacyForwardIterator*](https://en.cppreference.com/w/cpp/named_req/ForwardIterator) | ❌ no | Defined as podio `CollectionIterator`. `const_iterator::value_type` not defined, not [*LegacyForwardIterator*](https://en.cppreference.com/w/cpp/named_req/ForwardIterator) ([see below](#legacyforwarditerator)) | `difference_type`| Signed integer | Must be the same as `std::iterator_traits::difference_type` for `iterator` and `const_iterator` | ❌ no | `std::iterator_traits::difference_type` not defined | | `size_type` | Unsigned integer | Large enough to represent all positive values of `difference_type` | ✔️ yes | | @@ -20,29 +27,29 @@ The PODIO `Collection`s interface was designed to remind the standard *Container | Expression | Return type | Semantics | Fulfilled by Collection? | Comment | |------------|-------------|-----------|--------------------------|---------| -| `C()` | `C` | Creates an empty container | ✔️ yes | -| `C(a)` | `C` | Creates a copy of `a` | ❌ no | Not defined, non-copyable by design -| `C(rv)` | `C` | Moves `rv` | ✔️ yes | -| `a = b` | `C&` | Destroys or copy-assigns all elements of `a` from elements of `b` | ❌ no | Not defined, non-copyable by design -| `a = rv` | `C&` | Destroys or move-assigns all elements of `a` from elements of `rv` | ✔️ yes | -| `a.~C()` | `void` | Destroys all elements of `a` and frees all memory| ✔️ yes | -| `a.begin()` | `(const_)iterator` | Iterator to the first element of `a` | ✔️ yes | -| `a.end()` | `(const_)iterator` | Iterator to one past the last element of `a` | ✔️ yes | -| `a.cbegin()` | `const_iterator` | Same as `const_cast(a).begin()` | ✔️ yes | -| `a.cend()` | `const_iterator` | Same as `const_cast(a).end()`| ✔️ yes | +| `C()` | `C` | Creates an empty container | ✔️ yes | | +| `C(a)` | `C` | Creates a copy of `a` | ❌ no | Not defined, non-copyable by design | +| `C(rv)` | `C` | Moves `rv` | ✔️ yes | | +| `a = b` | `C&` | Destroys or copy-assigns all elements of `a` from elements of `b` | ❌ no | Not defined, non-copyable by design | +| `a = rv` | `C&` | Destroys or move-assigns all elements of `a` from elements of `rv` | ✔️ yes | | +| `a.~C()` | `void` | Destroys all elements of `a` and frees all memory| ✔️ yes | Invalidates all handles retrieved from this collection | +| `a.begin()` | `(const_)iterator` | Iterator to the first element of `a` | ✔️ yes | | +| `a.end()` | `(const_)iterator` | Iterator to one past the last element of `a` | ✔️ yes | | +| `a.cbegin()` | `const_iterator` | Same as `const_cast(a).begin()` | ✔️ yes | | +| `a.cend()` | `const_iterator` | Same as `const_cast(a).end()`| ✔️ yes | | | `a == b` | Convertible to `bool` | Same as `std::equal(a.begin(), a.end(), b.begin(), b.end())`| ❌ no | Not defined | | `a != b` | Convertible to `bool` | Same as `!(a == b)` | ❌ no | Not defined | | `a.swap(b)` | `void` | Exchanges the values of `a` and `b` | ❌ no | Not defined | -| `swap(a,b)` | `void` | Same as `a.swap(b)` | ✔️ yes | `a.swap(b)` not defined | -| `a.size()` | `size_type` | Same as `std::distance(a.begin(), a.end())` | ✔️ yes | +| `swap(a,b)` | `void` | Same as `a.swap(b)` | ❌ no | `a.swap(b)` not defined | +| `a.size()` | `size_type` | Same as `std::distance(a.begin(), a.end())` | ✔️ yes | | | `a.max_size()` | `size_type` | `b.size()` where b is the largest possible container | ✔️ yes | | -| `a.empty()` | Convertible to `bool` | Same as `a.begin() == a.end()` | ✔️ yes | +| `a.empty()` | Convertible to `bool` | Same as `a.begin() == a.end()` | ✔️ yes | | ## Collection as an *AllocatorAwareContainer* The C++ standard specifies [AllocatorAwareContainer](https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer) for containers that can use other allocators beside the default allocator. -PODIO collections don't provide customization point for allocators and use only the default allocator. Therefore they are not *AllocatorAwareContainers*. +PODIO collections don't provide a customization point for allocators and use only the default allocator. Therefore they are not *AllocatorAwareContainers*. ### AllocatorAwareContainer types @@ -58,6 +65,7 @@ The PODIO Collections currently are not checked against expression and statement The C++ specifies a set of named requirements for iterators. Starting with C++20 the standard specifies also iterator concepts. The requirements imposed by the concepts and named requirements are similar but not identical. +In the following tables a convention from `Collection` is used: `iterator` stands for PODIO `MutableCollectionIterator` and `const_iterator` stands for PODIO `CollectionIterator`. ### Iterator summary | Named requirement | `iterator` | `const_iterator` | @@ -166,8 +174,10 @@ In addition to the *LegacyForwardIterator* the C++ standard specifies also the * ## Collection and standard algorithms -Most of the standard algorithms require the iterators to be at least *InputIterator*. The iterators of PODIO collection don't fulfil this requirement, therefore they are not compatible with standard algorithms according to the specification. In practice some algorithms may still compile with the collections depending on an implementation of a given algorithm. +Most of the standard algorithms require the iterators to be at least *InputIterator*. The iterators of PODIO collection don't fulfil this requirement, therefore they are not compatible with standard algorithms according to the specification. + +In practice, some algorithms may still compile with the collections depending on the implementation of a given algorithm. In general, the standard **algorithms mutating a collection will give wrong results**, while the standard algorithms not mutating a collection in principle should give correct results if they compile. ## Standard range algorithms -The standard range algorithm use constrains to operate at least on `std::input_iterator`s and `std::ranges::input_range`s. The iterators of PODIO collection don't model these concepts, therefore can't be used with standard range algorithms. +The standard range algorithm use constrains to operate at least on `std::input_iterator`s and `std::ranges::input_range`s. The iterators of PODIO collection don't model these concepts, therefore can't be used with standard range algorithms. The range algorithms won't compile with PODIO `Collection` iterators.