Line data Source code
1 : /*
2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc.
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 : */
17 : #pragma once
18 :
19 : #include "logger.h"
20 :
21 : #include <map>
22 : #include <type_traits>
23 : #include <vector>
24 : #include <cassert>
25 :
26 : namespace jami {
27 :
28 : /**
29 : * This function adds a safe way to get an enum class size
30 : */
31 : template<typename A>
32 : constexpr inline size_t
33 504 : enum_class_size()
34 : {
35 504 : return size_t(A::COUNT__);
36 : }
37 :
38 : /**
39 : * This generic class represents a multidimensional enum class array.
40 : * It safely converts them to integers. Each enum class needs a "COUNT__" item
41 : * at the end."
42 : *
43 : * This struct enforces:
44 : * * That the rows are indexed using enum_classes
45 : * * That the size of the matrix matches the enum_class size
46 : * * That the operators are within the matrix boundary
47 : */
48 : template<class Row, typename Value, typename A = Value>
49 : struct Matrix1D
50 : {
51 : constexpr Matrix1D(std::initializer_list<std::initializer_list<Value>> s);
52 :
53 : // Row is a built-in type ("int" by default)
54 : Value operator[](Row v);
55 :
56 : const Value operator[](Row v) const;
57 :
58 : /**
59 : * An Iterator for enum classes
60 : */
61 : class EnumClassIter
62 : {
63 : public:
64 : EnumClassIter(const Matrix1D<Row, Value, A>* p_vec, size_t pos)
65 : : pos_(pos)
66 : , p_vec_(p_vec)
67 : {}
68 :
69 : bool operator!=(const EnumClassIter& other) const;
70 : Row operator*() const;
71 : const EnumClassIter& operator++();
72 :
73 : private:
74 : size_t pos_;
75 : const Matrix1D<Row, Value, A>* p_vec_;
76 : };
77 :
78 : // Iterators
79 : EnumClassIter begin();
80 : EnumClassIter end();
81 :
82 : // Only use for single reverse mappable arrays, will ASSERT otherwise
83 : Row fromValue(const Value& value) const;
84 :
85 : static void setReverseMapping(Matrix1D<Row, const char*> names);
86 :
87 : private:
88 : const std::vector<Value> data_;
89 : static std::map<A, Row> reverseMapping_;
90 : };
91 :
92 : /**
93 : * A matrix with no value
94 : *
95 : * This is useful to use enum class in C++11 foreach loops
96 : *
97 : * @usage
98 : * for (const MyEnum& value : Matrix0D<MyEnum>()) {
99 : * std::cout << "Name: " << MyEnumNames[value] << std::endl;
100 : * }
101 : */
102 : template<class EnumClass>
103 : struct Matrix0D
104 : {
105 : /**
106 : * An Iterator for enum classes
107 : */
108 : class EnumClassIter
109 : {
110 : public:
111 0 : EnumClassIter(const Matrix0D<EnumClass>* p_vec, size_t pos)
112 0 : : pos_(pos)
113 0 : , p_vec_(p_vec)
114 0 : {}
115 :
116 : bool operator!=(const EnumClassIter& other) const;
117 : EnumClass operator*() const;
118 : const EnumClassIter& operator++();
119 :
120 : private:
121 : size_t pos_;
122 : const Matrix0D<EnumClass>* p_vec_;
123 : };
124 :
125 : Matrix0D();
126 :
127 : // Iterators
128 : EnumClassIter begin();
129 : EnumClassIter end();
130 : };
131 :
132 : /**
133 : * A helper to type to match serializable string to enum elements
134 : */
135 : template<class Row>
136 : using EnumClassNames = Matrix1D<Row, std::string_view>;
137 :
138 : /**
139 : * Create a matrix type with 2 enum class dimensions M[I,J] = V
140 : * ^ ^ ^
141 : * | | |
142 : * Rows <--- | |
143 : * Columns <----- |
144 : * Value <----------
145 : */
146 : template<class Row, class Column, typename Value>
147 : using Matrix2D = Matrix1D<Row, Matrix1D<Column, Value>>;
148 :
149 : /**
150 : * Create an array of callbacks.
151 : *
152 : * This type hides all the C++ syntax requirements
153 : */
154 : template<class Row, class Class, typename Result = void, typename... Args>
155 : using CallbackMatrix1D = Matrix1D<Row, Result (Class::*)(Args... args)>;
156 :
157 : /**
158 : * Create a method callback matrix.
159 : *
160 : * This type hides all the C++ syntax requirements
161 : */
162 : template<class Row, class Column, class Class, typename Result = void, typename... Args>
163 : using CallbackMatrix2D = Matrix2D<Row, Column, void (Class::*)(Args... args)>;
164 :
165 : /*
166 : * IMPLEMENTATION
167 : *
168 : */
169 :
170 : template<class Row, typename Value, typename Accessor>
171 504 : constexpr Matrix1D<Row, Value, Accessor>::Matrix1D(std::initializer_list<std::initializer_list<Value>> s)
172 504 : : data_(*std::begin(s))
173 : {
174 : static_assert(std::is_enum<Row>(), "Row has to be an enum class");
175 : static_assert((int) Row::COUNT__ > 0, "Row need a COUNT__ element");
176 :
177 : // FIXME C++14, use static_assert and make the ctor constexpr
178 504 : assert(s.begin()->size()
179 : == enum_class_size<Row>()); //,"Matrix row have to match the enum class size");
180 504 : }
181 :
182 : template<class Row, typename Value, typename Accessor>
183 : Value Matrix1D<Row, Value, Accessor>::operator[](Row v)
184 : {
185 : // ASSERT(size_t(v) >= size_t(Row::COUNT__),"State Machine Out of Bounds\n");
186 : if (size_t(v) >= enum_class_size<Row>() || static_cast<int>(v) < 0) {
187 : JAMI_ERR("State Machine Out of Bounds %d\n", size_t(v));
188 : assert(false);
189 : throw v;
190 : }
191 : return data_[size_t(v)];
192 : }
193 :
194 : template<class Row, typename Value, typename Accessor>
195 0 : const Value Matrix1D<Row, Value, Accessor>::operator[](Row v) const
196 : {
197 0 : assert(size_t(v) <= enum_class_size<Row>() + 1 && size_t(v) >= 0); // COUNT__ is also valid
198 0 : if (size_t(v) >= enum_class_size<Row>()) {
199 0 : JAMI_ERR("State Machine Out of Bounds %zu\n", size_t(v));
200 0 : assert(false);
201 : throw v;
202 : }
203 0 : return data_[size_t(v)];
204 : }
205 :
206 : template<class E, class T, class A>
207 : std::map<A, E> Matrix1D<E, T, A>::reverseMapping_;
208 :
209 : template<class Row, typename Value, typename Accessor>
210 : void
211 : Matrix1D<Row, Value, Accessor>::setReverseMapping(Matrix1D<Row, const char*> names)
212 : {
213 : for (const Row row : Matrix0D<Row>())
214 : reverseMapping_[names[row]] = row;
215 : }
216 :
217 : template<class Row, typename Value, typename Accessor>
218 : Row
219 : Matrix1D<Row, Value, Accessor>::fromValue(const Value& value) const
220 : {
221 : if (!reverseMapping_.empty()) {
222 : for (int i = 0; i < enum_class_size<Row>(); i++) {
223 : const_cast<Matrix1D*>(this)->reverseMapping_[(*const_cast<Matrix1D*>(this))[(Row) i]]
224 : = static_cast<Row>(i);
225 : }
226 : assert(reverseMapping_.empty() == enum_class_size<Row>());
227 : }
228 : if (reverseMapping_.count(value) == 0) {
229 : throw value;
230 : }
231 : return reverseMapping_[value];
232 : }
233 :
234 : template<class EnumClass>
235 0 : Matrix0D<EnumClass>::Matrix0D()
236 : {
237 : static_assert(std::is_enum<EnumClass>(),
238 : "The first template parameter has to be an enum class\n");
239 0 : }
240 :
241 : template<class EnumClass>
242 0 : EnumClass Matrix0D<EnumClass>::EnumClassIter::operator*() const
243 : {
244 0 : assert(pos_ < enum_class_size<EnumClass>());
245 0 : return static_cast<EnumClass>(pos_);
246 : }
247 :
248 : template<class EnumClass>
249 : const typename Matrix0D<EnumClass>::EnumClassIter&
250 0 : Matrix0D<EnumClass>::EnumClassIter::operator++()
251 : {
252 0 : ++pos_;
253 0 : return *this;
254 : }
255 :
256 : template<class EnumClass>
257 : bool
258 0 : Matrix0D<EnumClass>::EnumClassIter::operator!=(const EnumClassIter& other) const
259 : {
260 0 : return pos_ != other.pos_;
261 : }
262 :
263 : template<class EnumClass>
264 : typename Matrix0D<EnumClass>::EnumClassIter
265 0 : Matrix0D<EnumClass>::begin()
266 : {
267 0 : return Matrix0D<EnumClass>::EnumClassIter(this, 0);
268 : }
269 :
270 : template<class EnumClass>
271 : typename Matrix0D<EnumClass>::EnumClassIter
272 0 : Matrix0D<EnumClass>::end()
273 : {
274 0 : return Matrix0D<EnumClass>::EnumClassIter(this, enum_class_size<EnumClass>());
275 : }
276 :
277 : template<class Row, typename Value, typename Accessor>
278 : const typename Matrix1D<Row, Value, Accessor>::EnumClassIter&
279 : Matrix1D<Row, Value, Accessor>::EnumClassIter::operator++()
280 : {
281 : ++pos_;
282 : return *this;
283 : }
284 :
285 : template<class Row, typename Value, typename Accessor>
286 : bool
287 : Matrix1D<Row, Value, Accessor>::EnumClassIter::operator!=(const EnumClassIter& other) const
288 : {
289 : return pos_ != other.pos_;
290 : }
291 :
292 : template<class Row, typename Value, typename Accessor>
293 : typename Matrix1D<Row, Value, Accessor>::EnumClassIter
294 : Matrix1D<Row, Value, Accessor>::begin()
295 : {
296 : return Matrix1D<Row, Value, Accessor>::EnumClassIter(this, 0);
297 : }
298 :
299 : template<class Row, typename Value, typename Accessor>
300 : typename Matrix1D<Row, Value, Accessor>::EnumClassIter
301 : Matrix1D<Row, Value, Accessor>::end()
302 : {
303 : return Matrix1D<Row, Value, Accessor>::EnumClassIter(this, enum_class_size<Row>());
304 : }
305 :
306 : } // namespace jami
|