Commit | Line | Data |
---|---|---|
b17ed2ad JG |
1 | /* |
2 | * Copyright (C) 2023 Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1-only | |
5 | * | |
6 | */ | |
7 | ||
8 | #ifndef LTTNG_CONTAINER_WRAPPER_H | |
9 | #define LTTNG_CONTAINER_WRAPPER_H | |
10 | ||
feef6f74 JG |
11 | #include <common/exception.hpp> |
12 | #include <common/format.hpp> | |
b17ed2ad JG |
13 | #include <common/macros.hpp> |
14 | ||
15 | #include <cstddef> | |
16 | #include <iterator> | |
17 | ||
18 | namespace lttng { | |
19 | namespace utils { | |
20 | ||
21 | /* | |
22 | * random_access_container_wrapper is a helper to provide an idiomatic C++ interface | |
23 | * from a C container API. ElementAccessorCallable and ElementCountAccessorCallable | |
24 | * are two functors which must be provided to allow access to the underlying elements | |
25 | * of the container and to its size. | |
26 | */ | |
27 | template <typename ContainerType, typename ElementType, typename ContainerOperations> | |
28 | class random_access_container_wrapper { | |
f74e820c | 29 | template <typename IteratorContainerType, typename IteratorElementType> |
b17ed2ad JG |
30 | class _iterator : public std::iterator<std::random_access_iterator_tag, std::size_t> { |
31 | public: | |
f74e820c | 32 | explicit _iterator(IteratorContainerType& container, std::size_t start_index = 0) : |
b17ed2ad JG |
33 | _container(container), _index(start_index) |
34 | { | |
35 | } | |
36 | ||
37 | _iterator& operator++() noexcept | |
38 | { | |
39 | ++_index; | |
40 | return *this; | |
41 | } | |
42 | ||
43 | _iterator& operator--() noexcept | |
44 | { | |
45 | --_index; | |
46 | return *this; | |
47 | } | |
48 | ||
49 | _iterator& operator++(int) noexcept | |
50 | { | |
51 | auto this_before_increment = *this; | |
52 | ||
53 | _index++; | |
54 | return this_before_increment; | |
55 | } | |
56 | ||
57 | _iterator& operator--(int) noexcept | |
58 | { | |
59 | _index--; | |
60 | return *this; | |
61 | } | |
62 | ||
d83ba9ba JG |
63 | ptrdiff_t operator-(const _iterator& other) const |
64 | { | |
65 | return _index - other._index; | |
66 | } | |
67 | ||
b17ed2ad JG |
68 | bool operator==(const _iterator& other) const noexcept |
69 | { | |
70 | return _index == other._index; | |
71 | } | |
72 | ||
73 | bool operator!=(const _iterator& other) const noexcept | |
74 | { | |
75 | return !(*this == other); | |
76 | } | |
77 | ||
f74e820c JG |
78 | typename std::conditional<std::is_pointer<IteratorElementType>::value, |
79 | IteratorElementType, | |
80 | IteratorElementType&>::type | |
feef6f74 | 81 | operator*() const |
b17ed2ad JG |
82 | { |
83 | return _container[_index]; | |
84 | } | |
85 | ||
86 | private: | |
f74e820c | 87 | IteratorContainerType& _container; |
b17ed2ad JG |
88 | std::size_t _index; |
89 | }; | |
90 | ||
f74e820c JG |
91 | using iterator = _iterator<random_access_container_wrapper, ElementType>; |
92 | using const_iterator = _iterator<const random_access_container_wrapper, const ElementType>; | |
b17ed2ad JG |
93 | |
94 | public: | |
f74e820c JG |
95 | explicit random_access_container_wrapper(ContainerType container) : |
96 | _container{ std::move(container) } | |
b17ed2ad JG |
97 | { |
98 | } | |
99 | ||
100 | iterator begin() noexcept | |
101 | { | |
102 | return iterator(*this); | |
103 | } | |
104 | ||
feef6f74 | 105 | iterator end() |
b17ed2ad | 106 | { |
58561706 | 107 | return iterator(*this, size()); |
b17ed2ad JG |
108 | } |
109 | ||
f74e820c JG |
110 | const_iterator begin() const noexcept |
111 | { | |
112 | return const_iterator(*this); | |
113 | } | |
114 | ||
feef6f74 | 115 | const_iterator end() const |
f74e820c | 116 | { |
58561706 | 117 | return const_iterator(*this, size()); |
f74e820c JG |
118 | } |
119 | ||
58561706 | 120 | std::size_t size() const |
b17ed2ad JG |
121 | { |
122 | return ContainerOperations::size(_container); | |
123 | } | |
124 | ||
d73aeddd JG |
125 | bool empty() const |
126 | { | |
127 | return size() == 0; | |
128 | } | |
129 | ||
b17ed2ad JG |
130 | typename std::conditional<std::is_pointer<ElementType>::value, ElementType, ElementType&>::type |
131 | operator[](std::size_t index) | |
132 | { | |
feef6f74 JG |
133 | /* |
134 | * To share code between the const and mutable versions of this operator, 'this' | |
135 | * is casted to a const reference. A const_cast then ensures that a mutable | |
136 | * reference (or pointer) is returned. | |
137 | * | |
138 | * We typically avoid const_cast, but this is safe: if the user is calling the | |
139 | * mutable version of this operator, it had a mutable object anyhow. | |
140 | * | |
141 | * For more information, see Item 3 of Effective C++. | |
142 | */ | |
69e98ad6 | 143 | const auto& const_this = static_cast<const random_access_container_wrapper&>(*this); |
feef6f74 JG |
144 | |
145 | /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) */ | |
146 | return const_cast<typename std::conditional<std::is_pointer<ElementType>::value, | |
147 | ElementType, | |
148 | ElementType&>::type>(const_this[index]); | |
b17ed2ad JG |
149 | } |
150 | ||
151 | typename std::conditional<std::is_pointer<ElementType>::value, | |
152 | const ElementType, | |
153 | const ElementType&>::type | |
154 | operator[](std::size_t index) const | |
155 | { | |
feef6f74 | 156 | if (index >= ContainerOperations::size(_container)) { |
d73aeddd | 157 | throw std::invalid_argument(lttng::format( |
feef6f74 JG |
158 | "Out of bound access through random_access_container_wrapper: index={}, size={}", |
159 | index, | |
160 | size())); | |
161 | } | |
162 | ||
b17ed2ad JG |
163 | return ContainerOperations::get(_container, index); |
164 | } | |
165 | ||
f74e820c | 166 | protected: |
b17ed2ad JG |
167 | ContainerType _container; |
168 | }; | |
169 | } /* namespace utils */ | |
170 | } /* namespace lttng */ | |
171 | ||
172 | #endif /* LTTNG_CONTAINER_WRAPPER_H */ |