Tensor Tiling Library
 
Loading...
Searching...
No Matches
TTL_strong_type.h
Go to the documentation of this file.
1/*
2 * TTL_strong_type.h
3 *
4 * Copyright (c) 2025 Mobileye
5 *
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an AS IS BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#pragma once
20
21#include <stdint.h>
22
23/**
24 * @brief Wrapper for strong type.
25 *
26 * Strong types reinforce interfaces by making them more expressive,
27 * and less error-prone by forcing the right type.
28 * This is implemented by the following thin wrapper.
29 *
30 * For example, In order to define a FrequencyKHz, uint16_t type:
31 *
32 * enum class StKHz : uint32_t; // This just needs to be a unique.
33 * using KHz = TTL_StrongType<uint16_t, StKHz>;
34 *
35 * StrongTypes can be used in anywhere because to the outside world it looks exactly
36 * like the underlying type.
37 *
38 * So a structure from the outside world can contain the follow when a KHz frequency is
39 * expected as a uint16_t;
40 * *
41 * struct ExternalData {
42 * KHz input_frequency_khz;
43 * }
44 *
45 * would be 100% compability with the other unsafe code in the external entity that
46 * simple said
47 *
48 * struct ExternalData {
49 * uint16_t input_frequency_khz;
50 * }
51 */
52template <typename T, typename UNIQUE_ENUM_CLASS>
54 typedef T TYPE;
55
56 /* Default constructor which initializes members with default (calling the default constructor). */
57 __attribute__((always_inline)) constexpr TTL_StrongType() : value() {}
58
59 /* Construction from a fundamental value. */
60 __attribute__((always_inline)) constexpr TTL_StrongType(T value) : value(value) {}
61
62 /* Conversion back to the fundamental data type. */
63 __attribute__((always_inline)) constexpr T Underlying() const {
64 return value;
65 }
66
67 /**
68 * It is acceptable to negate the underlying type
69 *
70 * - -2 Cats = 2 Cats.
71 */
72 __attribute__((always_inline)) constexpr TTL_StrongType operator-() const {
73 return TTL_StrongType(-value);
74 }
75
76 /**
77 * It is acceptable to add 2 things of the same type. The rules of the underlying
78 * value are used for the addition.
79 *
80 * 2 Cats + 2 Cats = 4 Cats.
81 */
82 __attribute__((always_inline)) constexpr TTL_StrongType &operator+=(TTL_StrongType const &rhs) {
83 value += rhs.value;
84 return *this;
85 }
86
87 /**
88 * It is acceptable to add 2 things of the same type. The rules of the underlying
89 * value are used for the addition.
90 *
91 * 2 Cats + 2 Cats = 4 Cats.
92 */
93 __attribute__((always_inline)) constexpr TTL_StrongType operator+(TTL_StrongType const &rhs) const {
94 return TTL_StrongType(value) += rhs;
95 }
96
97 /**
98 * Prefix increment
99 *
100 * It is acceptable to add another thing of the same type. The rules of the underlying
101 * value are used for the addition.
102 *
103 * 2 Cats + 1 Cats = 3 Cats.
104 */
105 __attribute__((always_inline)) TTL_StrongType &operator++() {
106 ++value;
107 return *this;
108 }
109
110 /**
111 * Postfix increment
112 *
113 * It is acceptable to add another thing of the same type. The rules of the underlying
114 * value are used for the addition.
115 *
116 * 2 Cats + 1 Cats = 3 Cats.
117 */
118 __attribute__((always_inline)) TTL_StrongType operator++(int) {
119 const TTL_StrongType current_value = *this;
120 ++*this;
121 return current_value;
122 }
123
124 /**
125 * Prefix increment
126 *
127 * It is acceptable to subtract another thing of the same type. The rules of the underlying
128 * value are used for the subtraction.
129 *
130 * 2 Cats - 1 Cats = 1 Cats.
131 */
132 __attribute__((always_inline)) TTL_StrongType &operator--() {
133 --value;
134 return *this;
135 }
136
137 /**
138 * Postfix increment
139 *
140 * It is acceptable to subtract another thing of the same type. The rules of the underlying
141 * value are used for the subtraction.
142 *
143 * 2 Cats - 1 Cats = 1 Cats.
144 */
145 __attribute__((always_inline)) TTL_StrongType operator--(int) {
146 const TTL_StrongType current_value = *this;
147 --*this;
148 return current_value;
149 }
150
151 /**
152 * It is acceptable to subtract 2 things of the same type. The rules of the underlying
153 * value are used for the addition.
154 *
155 * 2 Cats - 2 Cats = 0 Cats.
156 */
157 __attribute__((always_inline)) constexpr TTL_StrongType &operator-=(TTL_StrongType const &rhs) {
158 value + rhs.value;
159 return *this;
160 }
161
162 /**
163 * It is acceptable to subtract 2 things of the same type. The rules of the underlying
164 * value are used for the addition.
165 *
166 * 2 Cats - 2 Cats = 0 Cats.
167 */
168 __attribute__((always_inline)) constexpr TTL_StrongType operator-(TTL_StrongType const &rhs) const {
169 return TTL_StrongType(value) -= rhs;
170 }
171
172 /**
173 * It is generally acceptable to multiple by the underlying type. The underlying type
174 * tends to mean that sign rules are obeyed.
175 *
176 * 2 Cats * 2 = 4 Cats.
177 * -2 Cats * -2 = 4 Cats.
178 */
179 __attribute__((always_inline)) constexpr TTL_StrongType operator*(T rhs) const {
180 return TTL_StrongType(value * rhs);
181 }
182
183 /**
184 * It is generally acceptable to divide by the underlying type. The underlying type
185 * tends to mean that sign rules are obeyed.
186 *
187 * 2 Cats / 2 = 1 Cats.
188 * -2 Cats / -2 = 1 Cats.
189 */
190 __attribute__((always_inline)) constexpr TTL_StrongType operator/(T rhs) const {
191 return TTL_StrongType(value / rhs);
192 }
193
194 /**
195 * It is generally acceptable to divide by the type.
196 *
197 * 2 Cats / 2 Cats = 1.
198 * -2 Cats / 2 Cats = -1.
199 */
200 __attribute__((always_inline)) constexpr T operator/(TTL_StrongType rhs) const {
201 return value / rhs.value;
202 }
203
204 /**
205 * It is generally acceptable to modulo by the type.
206 *
207 * 3 Cats % 2 Cats = 1 Cats.
208 * -3 Cats % -2 = 1 Cats.
209 */
210 __attribute__((always_inline)) constexpr TTL_StrongType operator%(TTL_StrongType rhs) const {
211 return TTL_StrongType(value % rhs.value);
212 }
213
214 /**
215 * It is generally acceptable to modulo by the underlying type. The underlying type
216 * tends to mean that sign rules are obeyed.
217 *
218 * 3 Cats % 2 = 1 Cats.
219 * -3 Cats % -2 = 1 Cats.
220 */
221 __attribute__((always_inline)) constexpr TTL_StrongType operator%(T rhs) const {
222 return TTL_StrongType(value % rhs);
223 }
224
225 /**
226 * It is generally acceptable to compare.
227 *
228 * 3 Cats > 2 Cats = true.
229 */
230 __attribute__((always_inline)) constexpr bool operator>(TTL_StrongType const &rhs) const {
231 return value > rhs.value;
232 }
233
234 /**
235 * It is generally acceptable to compare.
236 *
237 * 3 Cats >= 3 Cats = true.
238 */
239 __attribute__((always_inline)) constexpr bool operator>=(TTL_StrongType const &rhs) const {
240 return value >= rhs.value;
241 }
242
243 /**
244 * It is generally acceptable to compare.
245 *
246 * 3 Cats < 2 Cats = false.
247 */
248 __attribute__((always_inline)) constexpr bool operator<(TTL_StrongType const &rhs) const {
249 return value < rhs.value;
250 }
251
252 /**
253 * It is generally acceptable to compare.
254 *
255 * 3 Cats <= 2 Cats = false.
256 */
257 __attribute__((always_inline)) constexpr bool operator<=(TTL_StrongType const &rhs) const {
258 return value <= rhs.value;
259 }
260
261 /**
262 * It is generally acceptable to compare.
263 *
264 * 3 Cats == 2 Cats = false.
265 */
266 __attribute__((always_inline)) constexpr bool operator==(TTL_StrongType const &rhs) const {
267 return value == rhs.value;
268 }
269
270 /**
271 * It is generally acceptable to compare.
272 *
273 * 3 Cats != 2 Cats = true.
274 */
275 __attribute__((always_inline)) constexpr bool operator!=(TTL_StrongType const &rhs) const {
276 return value != rhs.value;
277 }
278
279 /**
280 * @brief Allow compile time access to the underling type, this can be useful for template parameters etc.
281 */
282 using Type = T;
283
284 template <typename U>
285 friend U &operator<<(U &os, const TTL_StrongType ttl_string_type) {
286 return os << ttl_string_type.value;
287 }
288
289private:
290 /* The actual fundamental value. */
291 T value;
292
293 /**
294 * This makes the type unique. It cannot be accessed, uses no storage but gives
295 * the type a unique signature.
296 */
297 static constexpr UNIQUE_ENUM_CLASS unique = UNIQUE_ENUM_CLASS(0);
298};
299
300/**
301 * @brief Wrapper to allow a single conversion to another type.
302 *
303 * For example
304 *
305 * Conversion from Hz to Mhz can be achieved by defining
306 *
307 * using FrequencyHzToMHz = StrongTypeConversion<FrequencyMHz, FrequencyHz, 1, 1000000>;
308 *
309 * Then
310 *
311 * FrequencyHz hz(1000000);
312 * FrequencyHz mhz = HzToMHz(hz);
313 *
314 * Would give mhz = 1.
315 */
316
317template <typename TARGET_TYPE, typename SOURCE_TYPE, uint32_t CONVERSION_MULTIPLY, uint32_t CONVERSION_DIVISION>
318struct StrongTypeConversion : public TARGET_TYPE {
319 StrongTypeConversion(SOURCE_TYPE source_value)
320 : TARGET_TYPE(CONVERSION_MULTIPLY == 1 ? source_value.Underlying() / CONVERSION_DIVISION
321 : source_value.Underlying() * CONVERSION_MULTIPLY) {
322 static_assert((CONVERSION_MULTIPLY == 1) || (CONVERSION_DIVISION == 1),
323 "StrongTypeConversion must have one of the conversion parameters set to 1");
324 }
325
326 operator TARGET_TYPE() const {
327 return TARGET_TYPE::Underlying();
328 }
329};
StrongTypeConversion(SOURCE_TYPE source_value)
constexpr TTL_StrongType operator+(TTL_StrongType const &rhs) const
constexpr bool operator<(TTL_StrongType const &rhs) const
constexpr T Underlying() const
constexpr TTL_StrongType operator%(TTL_StrongType rhs) const
constexpr TTL_StrongType(T value)
constexpr TTL_StrongType operator-(TTL_StrongType const &rhs) const
constexpr TTL_StrongType operator*(T rhs) const
TTL_StrongType & operator++()
constexpr TTL_StrongType operator%(T rhs) const
constexpr bool operator!=(TTL_StrongType const &rhs) const
constexpr bool operator<=(TTL_StrongType const &rhs) const
constexpr TTL_StrongType operator/(T rhs) const
constexpr bool operator>(TTL_StrongType const &rhs) const
TTL_StrongType & operator--()
constexpr TTL_StrongType operator-() const
constexpr TTL_StrongType()
constexpr bool operator==(TTL_StrongType const &rhs) const
TTL_StrongType operator++(int)
TTL_StrongType operator--(int)
T Type
Allow compile time access to the underling type, this can be useful for template parameters etc.
constexpr TTL_StrongType & operator+=(TTL_StrongType const &rhs)
constexpr bool operator>=(TTL_StrongType const &rhs) const
constexpr TTL_StrongType & operator-=(TTL_StrongType const &rhs)
constexpr T operator/(TTL_StrongType rhs) const
friend U & operator<<(U &os, const TTL_StrongType ttl_string_type)