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 | ||
63 | bool operator==(const _iterator& other) const noexcept | |
64 | { | |
65 | return _index == other._index; | |
66 | } | |
67 | ||
68 | bool operator!=(const _iterator& other) const noexcept | |
69 | { | |
70 | return !(*this == other); | |
71 | } | |
72 | ||
f74e820c JG |
73 | typename std::conditional<std::is_pointer<IteratorElementType>::value, |
74 | IteratorElementType, | |
75 | IteratorElementType&>::type | |
feef6f74 | 76 | operator*() const |
b17ed2ad JG |
77 | { |
78 | return _container[_index]; | |
79 | } | |
80 | ||
81 | private: | |
f74e820c | 82 | IteratorContainerType& _container; |
b17ed2ad JG |
83 | std::size_t _index; |
84 | }; | |
85 | ||
f74e820c JG |
86 | using iterator = _iterator<random_access_container_wrapper, ElementType>; |
87 | using const_iterator = _iterator<const random_access_container_wrapper, const ElementType>; | |
b17ed2ad JG |
88 | |
89 | public: | |
f74e820c JG |
90 | explicit random_access_container_wrapper(ContainerType container) : |
91 | _container{ std::move(container) } | |
b17ed2ad JG |
92 | { |
93 | } | |
94 | ||
95 | iterator begin() noexcept | |
96 | { | |
97 | return iterator(*this); | |
98 | } | |
99 | ||
feef6f74 | 100 | iterator end() |
b17ed2ad | 101 | { |
58561706 | 102 | return iterator(*this, size()); |
b17ed2ad JG |
103 | } |
104 | ||
f74e820c JG |
105 | const_iterator begin() const noexcept |
106 | { | |
107 | return const_iterator(*this); | |
108 | } | |
109 | ||
feef6f74 | 110 | const_iterator end() const |
f74e820c | 111 | { |
58561706 | 112 | return const_iterator(*this, size()); |
f74e820c JG |
113 | } |
114 | ||
58561706 | 115 | std::size_t size() const |
b17ed2ad JG |
116 | { |
117 | return ContainerOperations::size(_container); | |
118 | } | |
119 | ||
120 | typename std::conditional<std::is_pointer<ElementType>::value, ElementType, ElementType&>::type | |
121 | operator[](std::size_t index) | |
122 | { | |
feef6f74 JG |
123 | /* |
124 | * To share code between the const and mutable versions of this operator, 'this' | |
125 | * is casted to a const reference. A const_cast then ensures that a mutable | |
126 | * reference (or pointer) is returned. | |
127 | * | |
128 | * We typically avoid const_cast, but this is safe: if the user is calling the | |
129 | * mutable version of this operator, it had a mutable object anyhow. | |
130 | * | |
131 | * For more information, see Item 3 of Effective C++. | |
132 | */ | |
69e98ad6 | 133 | const auto& const_this = static_cast<const random_access_container_wrapper&>(*this); |
feef6f74 JG |
134 | |
135 | /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) */ | |
136 | return const_cast<typename std::conditional<std::is_pointer<ElementType>::value, | |
137 | ElementType, | |
138 | ElementType&>::type>(const_this[index]); | |
b17ed2ad JG |
139 | } |
140 | ||
141 | typename std::conditional<std::is_pointer<ElementType>::value, | |
142 | const ElementType, | |
143 | const ElementType&>::type | |
144 | operator[](std::size_t index) const | |
145 | { | |
feef6f74 | 146 | if (index >= ContainerOperations::size(_container)) { |
f9a41357 | 147 | LTTNG_THROW_INVALID_ARGUMENT_ERROR(lttng::format( |
feef6f74 JG |
148 | "Out of bound access through random_access_container_wrapper: index={}, size={}", |
149 | index, | |
150 | size())); | |
151 | } | |
152 | ||
b17ed2ad JG |
153 | return ContainerOperations::get(_container, index); |
154 | } | |
155 | ||
f74e820c | 156 | protected: |
b17ed2ad JG |
157 | ContainerType _container; |
158 | }; | |
159 | } /* namespace utils */ | |
160 | } /* namespace lttng */ | |
161 | ||
162 | #endif /* LTTNG_CONTAINER_WRAPPER_H */ |