|
| 1 | +# PODIO Collection as a *Container* |
| 2 | + |
| 3 | +Comparison of the PODIO `Collection`s with a C++ named requirement [*Container*](https://en.cppreference.com/w/cpp/named_req/Container). |
| 4 | + |
| 5 | +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*. |
| 6 | + |
| 7 | +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. |
| 8 | +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. |
| 9 | + |
| 10 | +As a consequence some of the **standard algorithms may not work** with PODIO `Collection` iterators. See [standard algorithm documentation](#collection-and-standard-algorithms) below. |
| 11 | + |
| 12 | +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. |
| 13 | + |
| 14 | +### Container Types |
| 15 | + |
| 16 | +| Name | Type | Requirements | Fulfilled by Collection? | Comment | |
| 17 | +|------|------|--------------|--------------------------|---------| |
| 18 | +| `value_type` | `T` | *[Erasable](https://en.cppreference.com/w/cpp/named_req/Erasable)* | ✔️ yes | Defined as an immutable user layer object type | |
| 19 | +| `reference` | `T&` | | ❌ no | Not defined | |
| 20 | +| `const_reference` | `const T&` | | ❌ no | Not defined | |
| 21 | +| `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`| |
| 22 | +| `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)) |
| 23 | +| `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 | |
| 24 | +| `size_type` | Unsigned integer | Large enough to represent all positive values of `difference_type` | ✔️ yes | | |
| 25 | + |
| 26 | +### Container member functions and operators |
| 27 | + |
| 28 | +| Expression | Return type | Semantics | Fulfilled by Collection? | Comment | |
| 29 | +|------------|-------------|-----------|--------------------------|---------| |
| 30 | +| `C()` | `C` | Creates an empty container | ✔️ yes | | |
| 31 | +| `C(a)` | `C` | Creates a copy of `a` | ❌ no | Not defined, non-copyable by design | |
| 32 | +| `C(rv)` | `C` | Moves `rv` | ✔️ yes | | |
| 33 | +| `a = b` | `C&` | Destroys or copy-assigns all elements of `a` from elements of `b` | ❌ no | Not defined, non-copyable by design | |
| 34 | +| `a = rv` | `C&` | Destroys or move-assigns all elements of `a` from elements of `rv` | ✔️ yes | | |
| 35 | +| `a.~C()` | `void` | Destroys all elements of `a` and frees all memory| ✔️ yes | Invalidates all handles retrieved from this collection | |
| 36 | +| `a.begin()` | `(const_)iterator` | Iterator to the first element of `a` | ✔️ yes | | |
| 37 | +| `a.end()` | `(const_)iterator` | Iterator to one past the last element of `a` | ✔️ yes | | |
| 38 | +| `a.cbegin()` | `const_iterator` | Same as `const_cast<const C&>(a).begin()` | ✔️ yes | | |
| 39 | +| `a.cend()` | `const_iterator` | Same as `const_cast<const C&>(a).end()`| ✔️ yes | | |
| 40 | +| `a == b` | Convertible to `bool` | Same as `std::equal(a.begin(), a.end(), b.begin(), b.end())`| ❌ no | Not defined | |
| 41 | +| `a != b` | Convertible to `bool` | Same as `!(a == b)` | ❌ no | Not defined | |
| 42 | +| `a.swap(b)` | `void` | Exchanges the values of `a` and `b` | ❌ no | Not defined | |
| 43 | +| `swap(a,b)` | `void` | Same as `a.swap(b)` | ❌ no | `a.swap(b)` not defined | |
| 44 | +| `a.size()` | `size_type` | Same as `std::distance(a.begin(), a.end())` | ✔️ yes | | |
| 45 | +| `a.max_size()` | `size_type` | `b.size()` where b is the largest possible container | ✔️ yes | | |
| 46 | +| `a.empty()` | Convertible to `bool` | Same as `a.begin() == a.end()` | ✔️ yes | | |
| 47 | + |
| 48 | +## Collection as an *AllocatorAwareContainer* |
| 49 | + |
| 50 | +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. |
| 51 | + |
| 52 | +PODIO collections don't provide a customization point for allocators and use only the default allocator. Therefore they are not *AllocatorAwareContainers*. |
| 53 | + |
| 54 | +### AllocatorAwareContainer types |
| 55 | + |
| 56 | +| Name | Requirements | Fulfilled by Collection? | Comment | |
| 57 | +|------|--------------|--------------------------|---------| |
| 58 | +| `allocator_type` | `allocator_type::value_type` same as `value_type` | ❌ no | `allocator_type` not defined | |
| 59 | + |
| 60 | +### *AllocatorAwareContainer* expression and statements |
| 61 | + |
| 62 | +The PODIO Collections currently are not checked against expression and statements requirements for *AllocatorAwareContainer*. |
| 63 | + |
| 64 | +## Collection iterators as an *Iterator* |
| 65 | + |
| 66 | +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. |
| 67 | + |
| 68 | +In the following tables a convention from `Collection` is used: `iterator` stands for PODIO `MutableCollectionIterator` and `const_iterator` stands for PODIO `CollectionIterator`. |
| 69 | +### Iterator summary |
| 70 | + |
| 71 | +| Named requirement | `iterator` | `const_iterator` | |
| 72 | +|-------------------|-----------------------|-----------------------------| |
| 73 | +| [LegacyIterator](https://en.cppreference.com/w/cpp/named_req/Iterator) | ❌ no ([see below](#legacyiterator)) | ❌ no ([see below](#legacyiterator)) | |
| 74 | +| [LegacyInputIterator](https://en.cppreference.com/w/cpp/named_req/InputIterator) | ❌ no ([see below](#legacyinputiterator)) | ❌ no ([see below](#legacyinputiterator)) | |
| 75 | +| [LegacyForwardIterator](https://en.cppreference.com/w/cpp/named_req/ForwardIterator) | ❌ no ([see below](#legacyforwarditerator)) | ❌ no ([see below](#legacyforwarditerator)) | |
| 76 | +| [LegacyOutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator) | ❌ no ([see below](#legacyoutputiterator)) | ❌ no ([see below](#legacyoutputiterator)) | |
| 77 | +| [LegacyBidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator) | ❌ no | ❌ no | |
| 78 | +| [LegacyRandomAccessIterator](https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator) | ❌ no | ❌ no | |
| 79 | +| [LegacyContiguousIterator](https://en.cppreference.com/w/cpp/named_req/ContiguousIterator) | ❌ no | ❌ no | |
| 80 | + |
| 81 | +| Concept | `iterator` | `const_iterator` | |
| 82 | +|---------|------------------------|------------------------------| |
| 83 | +| `std::indirectly_readable` | ❌ no | ❌ no | |
| 84 | +| `std::indirectly_writable` | ❌ no | ❌ no | |
| 85 | +| `std::weakly_incrementable` | ❌ no | ❌ no | |
| 86 | +| `std::incrementable` | ❌ no | ❌ no | |
| 87 | +| `std::input_or_output_iterator` | ❌ no | ❌ no | |
| 88 | +| `std::input_iterator` | ❌ no | ❌ no | |
| 89 | +| `std::output_iterator` | ❌ no | ❌ no | |
| 90 | +| `std::forward_iterator` | ❌ no | ❌ no | |
| 91 | +| `std::bidirectional_iterator` | ❌ no | ❌ no | |
| 92 | +| `std::random_access_iterator` | ❌ no | ❌ no | |
| 93 | +| `std::contiguous_iterator` | ❌ no | ❌ no | |
| 94 | + |
| 95 | +### LegacyIterator |
| 96 | + |
| 97 | +| Requirement | Fulfilled by `iterator`/`const_iterator`? | Comment | |
| 98 | +|-------------|-------------------------------------------|---------| |
| 99 | +| [*CopyConstructible*](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) | ❌ no / ❌ no | Move constructor and copy constructor not defined | |
| 100 | +| [*CopyAssignable*](https://en.cppreference.com/w/cpp/named_req/CopyAssignable) | ❌ no / ❌ no | Move assignment and copy assignment not defined | |
| 101 | +| [*Destructible*](https://en.cppreference.com/w/cpp/named_req/Destructible) | ✔️ yes / ✔️ yes | | |
| 102 | +| [*Swappable*](https://en.cppreference.com/w/cpp/named_req/Swappable) | ❌ no / ❌ no | | |
| 103 | +| `std::iterator_traits::value_type` (Until C++20 ) | ❌ no / ❌ no | Not defined | |
| 104 | +| `std::iterator_traits::difference_type` | ❌ no / ❌ no | Not defined | |
| 105 | +| `std::iterator_traits::reference` | ❌ no / ❌ no | Not defined | |
| 106 | +| `std::iterator_traits::pointer` | ❌ no / ❌ no | Not defined | |
| 107 | +| `std::iterator_traits::iterator_category` | ❌ no / ❌ no | Not defined | |
| 108 | + |
| 109 | +| Expression | Return type | Semantics | Fulfilled by `iterator`/`const_iterator`? | Comment | |
| 110 | +|------------|-------------|-----------|-------------------------------------------|---------| |
| 111 | +| `*r` | Unspecified | | ✔️ yes / ✔️ yes | | |
| 112 | +| `++r` | `It&` | | ✔️ yes / ✔️ yes | | |
| 113 | + |
| 114 | +### LegacyInputIterator |
| 115 | + |
| 116 | +| Requirement | Fulfilled by `iterator`/`const_iterator`? | Comment | |
| 117 | +|-------------|-------------------------------------------|---------| |
| 118 | +| [*LegacyIterator*](https://en.cppreference.com/w/cpp/named_req/Iterator) | ❌ no / ❌ no | [See above](#legacyiterator) | |
| 119 | +| [*EqualityComparable*](https://en.cppreference.com/w/cpp/named_req/EqualityComparable) | ✔️ yes / ✔️ yes | | |
| 120 | + |
| 121 | +| Expression | Return type | Semantics | Fulfilled by `iterator`/`const_iterator`? | Comment | |
| 122 | +|------------|-------------|-----------|-------------------------------------------|---------| |
| 123 | +| `i != j` | Contextually convertible to `bool` | Same as `!(i==j)` | ✔️ yes / ✔️ yes | | |
| 124 | +| `*i` | `reference`, convertible to `value_type` | | ❌ no / ❌ no | `reference` and `value_type` not defined | |
| 125 | +| `i->m` | | Same as `(*i).m` | ✔️ yes / ✔️ yes | | |
| 126 | +| `++r` | `It&` | | ✔️ yes / ✔️ yes | | |
| 127 | +| `(void)r++` | | Same as `(void)++r` | ❌ no / ❌ no | Post-increment not defined | |
| 128 | +| `*r++` | Convertible to `value_type` | Same as `value_type x = *r; ++r; return x;` | ❌ no / ❌ no | Post-increment and `value_type` not defined | |
| 129 | + |
| 130 | +### LegacyForwardIterator |
| 131 | + |
| 132 | +In addition to the *LegacyForwardIterator* the C++ standard specifies also the *mutable LegacyForwardIterator*, which is both *LegacyForwardIterator* and *LegacyOutputIterator*. The term **mutable** used in this context doesn't imply mutability in the sense used in the PODIO. |
| 133 | + |
| 134 | + |
| 135 | +| Requirement | Fulfilled by `iterator`/`const_iterator`? | Comment | |
| 136 | +|-------------|-------------------------------------------|---------| |
| 137 | +| [*LegacyInputIterator*](https://en.cppreference.com/w/cpp/named_req/InputIterator) | ❌ no / ❌ no | [See above](#legacyinputiterator)| |
| 138 | +| [*DefaultConstructible*](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible) | ❌ no / ❌ no | Value initialization not defined | |
| 139 | +| If *mutable* iterator then `reference` same as `value_type&` or `value_type&&`, otherwise same as `const value_type&` or `const value_type&&` | ❌ no / ❌ no | `reference` and `value_type` not defined | |
| 140 | +| [Multipass guarantee](https://en.cppreference.com/w/cpp/named_req/ForwardIterator) | ❌ no / ❌ no | Copy constructor not defined | |
| 141 | +| [Singular iterators](https://en.cppreference.com/w/cpp/named_req/ForwardIterator) | ❌ no / ❌ no | Value initialization not defined | |
| 142 | + |
| 143 | +| Expression | Return type | Semantics | Fulfilled by `iterator`/`const_iterator`? | Comment | |
| 144 | +|------------|-------------|-----------|-------------------------------------------|---------| |
| 145 | +| `i++` | `It` | Same as `It ip = i; ++i; return ip;` | ❌ no / ❌ no | Post-increment not defined | |
| 146 | +| `*i++` | `reference` | | ❌ no / ❌ no | Post-increment and `reference` not defined | |
| 147 | + |
| 148 | +### LegacyOutputIterator |
| 149 | + |
| 150 | +| Requirement | Fulfilled by `iterator`/`const_iterator`? | Comment | |
| 151 | +|-------------|-------------------------------------------|---------| |
| 152 | +| [*LegacyIterator*](https://en.cppreference.com/w/cpp/named_req/Iterator) | ❌ no / ❌ no | [See above](#legacyiterator) | |
| 153 | +| Is pointer type or class type | ✔️ yes / ✔️ yes | | |
| 154 | + |
| 155 | +| Expression | Return type | Semantics | Fulfilled by `iterator`/`const_iterator`? | Comment | |
| 156 | +|------------|-------------|-----------|-------------------------------------------|---------| |
| 157 | +| `*r = o` | | | ❗ attention / ❗ attention | Defined but an assignment doesn't modify objects inside collection | |
| 158 | +| `++r` | `It&` | | ✔️ yes / ✔️ yes | | |
| 159 | +| `r++` | Convertible to `const It&` | Same as `It temp = r; ++r; return temp;` | ❌ no / ❌ no | Post-increment not defined | |
| 160 | +| `*r++ = o` | | Same as `*r = o; ++r;`| ❌ no / ❌ no | Post-increment not defined | |
| 161 | + |
| 162 | +## Collection iterators and standard iterator adaptors |
| 163 | + |
| 164 | +| Adaptor | Compatible with Collection? | Comment | |
| 165 | +|---------|-----------------------------|---------| |
| 166 | +| `std::reverse_iterator` | ❌ no | `iterator` and `const_iterator` not *LegacyBidirectionalIterator* or `std::bidirectional_iterator` | |
| 167 | +| `std::back_insert_iterator` | ❗ attention | Compatible only with SubsetCollections, otherwise throws `std::invalid_argument` | |
| 168 | +| `std::front_insert_iterator` | ❌ no | `push_front` not defined | |
| 169 | +| `std::insert_iterator` | ❌ no | `insert` not defined | |
| 170 | +| `std::const_iterator` | ❌ no | `iterator` and `const_iterator` not *LegacyInputIterator* or `std::input_iterator` | |
| 171 | +| `std::move_iterator` | ❌ no | `iterator` and `const_iterator` not *LegacyInputIterator* or `std::input_iterator` | |
| 172 | +| `std::counted_iterator` | ❌ no | `iterator` and `const_iterator` not `std::input_or_output_iterator` | |
| 173 | + |
| 174 | + |
| 175 | +## Collection and standard algorithms |
| 176 | + |
| 177 | +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. |
| 178 | + |
| 179 | +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. |
| 180 | + |
| 181 | +## Standard range algorithms |
| 182 | + |
| 183 | +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. |
0 commit comments