1 /*
2 * templates.h
3 * Copyright 2014-2019 John Lindgren
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions, and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions, and the following disclaimer in the documentation
13 * provided with the distribution.
14 *
15 * This software is provided "as is" and without any warranty, express or
16 * implied. In no event shall the authors be liable for any damages arising from
17 * the use of this software.
18 */
19
20 #ifndef LIBAUDCORE_TEMPLATES_H
21 #define LIBAUDCORE_TEMPLATES_H
22
23 #include <new>
24 #include <type_traits>
25 #include <utility>
26
27 // #undef POSIX and Windows macros to avoid name conflicts
28 #undef abs
29 #undef min
30 #undef max
31
32 namespace aud
33 {
34
35 // Utility functions
36 // =================
37
38 // minimum of two numbers
39 template<class T>
min(T a,T b)40 constexpr T min(T a, T b)
41 {
42 return a < b ? a : b;
43 }
44
45 // maximum of two numbers
46 template<class T>
max(T a,T b)47 constexpr T max(T a, T b)
48 {
49 return a > b ? a : b;
50 }
51
52 // make sure a number is within the given range
53 template<class T>
clamp(T x,T low,T high)54 constexpr T clamp(T x, T low, T high)
55 {
56 return min(max(x, low), high);
57 }
58
59 // absolute value
60 template<class T>
abs(T x)61 constexpr T abs(T x)
62 {
63 return x < 0 ? -x : x;
64 }
65
66 // change the sign of x to the sign of s
67 template<class T>
chsign(T x,T s)68 constexpr T chsign(T x, T s)
69 {
70 return (x < 0) ^ (s < 0) ? -x : x;
71 }
72
73 // integer division with rounding
74 template<class T>
rdiv(T x,T y)75 constexpr T rdiv(T x, T y)
76 {
77 return (x + chsign(y / 2, x)) / y;
78 }
79
80 // convert integer from one scale to another, with rounding
81 template<class T>
rescale(T x,T old_scale,T new_scale)82 constexpr T rescale(T x, T old_scale, T new_scale)
83 {
84 return rdiv(x * new_scale, old_scale);
85 }
86
87 // number of characters needed to represent an integer (including minus sign)
88 template<class T>
n_digits(T x)89 constexpr T n_digits(T x)
90 {
91 return x < 0 ? 1 + n_digits(-x) : x < 10 ? 1 : 1 + n_digits(x / 10);
92 }
93
94 // number of elements in an array
95 template<class T, int N>
n_elems(const T (&)[N])96 constexpr int n_elems(const T (&)[N])
97 {
98 return N;
99 }
100
101 // Casts for storing various data in a void pointer
102 // ================================================
103
104 template<class T>
to_ptr(T t)105 inline void * to_ptr(T t)
106 {
107 union
108 {
109 void * v;
110 T t;
111 } u = {nullptr};
112 static_assert(sizeof u == sizeof(void *),
113 "Type cannot be stored in a pointer");
114 u.t = t;
115 return u.v;
116 }
117
118 template<class T>
from_ptr(void * v)119 inline T from_ptr(void * v)
120 {
121 union
122 {
123 void * v;
124 T t;
125 } u = {v};
126 static_assert(sizeof u == sizeof(void *),
127 "Type cannot be stored in a pointer");
128 return u.t;
129 }
130
131 // Move-assignment implemented via move-constructor
132 // ================================================
133
134 template<class T>
move_assign(T & a,T && b)135 T & move_assign(T & a, T && b)
136 {
137 if (&a != &b)
138 {
139 a.~T();
140 new (&a) T(std::move(b));
141 }
142 return a;
143 }
144
145 // Function wrappers (or "casts") for interaction with C-style APIs
146 // ================================================================
147
148 template<class T>
delete_obj(void * obj)149 void delete_obj(void * obj)
150 {
151 (void)sizeof(T);
152 delete (T *)obj;
153 }
154
155 template<class T>
delete_typed(T * obj)156 void delete_typed(T * obj)
157 {
158 (void)sizeof(T);
159 delete obj;
160 }
161
162 template<class T, void (*func)(void *)>
typed_func(T * obj)163 void typed_func(T * obj)
164 {
165 func(obj);
166 }
167
168 template<class T, void (T::*func)()>
obj_member(void * obj)169 static void obj_member(void * obj)
170 {
171 (((T *)obj)->*func)();
172 }
173 template<class T, void (T::*func)() const>
obj_member(void * obj)174 static void obj_member(void * obj)
175 {
176 (((T *)obj)->*func)();
177 }
178
179 // Class which provides scope-based "ownership" of an object
180 // (similar to std::unique_lock, but more flexible)
181 // =========================================================
182
183 template<class T, void (T::*acquire)(), void (T::*release)()>
184 class owner
185 {
186 public:
m_obj(obj)187 explicit owner(T * obj = nullptr) : m_obj(obj)
188 {
189 if (m_obj)
190 (m_obj->*acquire)();
191 }
~owner()192 ~owner()
193 {
194 if (m_obj)
195 (m_obj->*release)();
196 }
197
owner(owner && b)198 owner(owner && b) : m_obj(b.m_obj) { b.m_obj = nullptr; }
199 owner & operator=(owner && b) { return move_assign(*this, std::move(b)); }
200
201 private:
202 T * m_obj;
203 };
204
205 // Wrapper class allowing enumerations to be used as array indexes;
206 // the enumeration must begin with zero and have a "count" constant
207 // ================================================================
208
209 template<class T, class V>
210 struct array
211 {
212 // cannot use std::forward here; it is not constexpr until C++14
213 template<class... Args>
arrayarray214 constexpr array(Args &&... args) : vals{static_cast<Args &&>(args)...}
215 {
216 }
217
218 // Due to GCC bug #63707, the forwarding constructor given above cannot be
219 // used to initialize the array when V is a class with no copy constructor.
220 // As a very limited workaround, provide a second constructor which can be
221 // used to initialize the array to default values in this case.
arrayarray222 constexpr array() : vals() {}
223
224 constexpr const V & operator[](T t) const { return vals[(int)t]; }
beginarray225 constexpr const V * begin() const { return vals; }
endarray226 constexpr const V * end() const { return vals + (int)T::count; }
227 V & operator[](T t) { return vals[(int)t]; }
beginarray228 V * begin() { return vals; }
endarray229 V * end() { return vals + (int)T::count; }
230
231 private:
232 V vals[(int)T::count];
233 };
234
235 // Wrapper class allowing enumerations to be used in range-based for loops
236 // =======================================================================
237
238 template<class T, T first = (T)0, T last = (T)((int)T::count - 1)>
239 struct range
240 {
241 struct iter
242 {
243 T v;
244 constexpr T operator*() const { return v; }
245 constexpr bool operator!=(iter other) const { return v != other.v; }
246 void operator++() { v = (T)((int)v + 1); }
247 };
248
beginrange249 static constexpr iter begin() { return {first}; }
endrange250 static constexpr iter end() { return {(T)((int)last + 1)}; }
251 };
252
253 // Replacement for std::allocator::construct, which also supports aggregate
254 // initialization. For background, see:
255 // http://cplusplus.github.io/LWG/lwg-active.html#2089
256 // ========================================================================
257
258 // class constructor proxy
259 template<class T, bool aggregate>
260 struct construct_base
261 {
262 template<class... Args>
makeconstruct_base263 static T * make(void * loc, Args &&... args)
264 {
265 return new (loc) T(std::forward<Args>(args)...);
266 }
267 };
268
269 // aggregate constructor proxy
270 template<class T>
271 struct construct_base<T, true>
272 {
273 template<class... Args>
274 static T * make(void * loc, Args &&... args)
275 {
276 return new (loc) T{std::forward<Args>(args)...};
277 }
278 };
279
280 // generic constructor proxy
281 template<class T>
282 struct construct
283 {
284 template<class... Args>
285 static T * make(void * loc, Args &&... args)
286 {
287 constexpr bool aggregate = !std::is_constructible<T, Args &&...>::value;
288 return construct_base<T, aggregate>::make(loc,
289 std::forward<Args>(args)...);
290 }
291 };
292
293 // Convert an integer constant to a string at compile-time; can be used for
294 // #defines, enums, constexpr calculations, etc.
295 // ========================================================================
296
297 // "metaprogramming" string type: each different string is a unique type
298 template<char... args>
299 struct metastring
300 {
301 char data[sizeof...(args) + 1] = {args..., '\0'};
302 };
303
304 // recursive number-printing template, general case (three or more digits)
305 template<int size, int x, char... args>
306 struct numeric_builder
307 {
308 typedef typename numeric_builder<size - 1, x / 10, '0' + abs(x) % 10,
309 args...>::type type;
310 };
311
312 // special case for two digits; minus sign is handled here
313 template<int x, char... args>
314 struct numeric_builder<2, x, args...>
315 {
316 typedef metastring <
317 x<0 ? '-' : '0' + x / 10, '0' + abs(x) % 10, args...> type;
318 };
319
320 // special case for one digit (positive numbers only)
321 template<int x, char... args>
322 struct numeric_builder<1, x, args...>
323 {
324 typedef metastring<'0' + x, args...> type;
325 };
326
327 // convenience wrapper for numeric_builder
328 template<int x>
329 class numeric_string
330 {
331 private:
332 // generate a unique string type representing this number
333 typedef typename numeric_builder<n_digits(x), x>::type type;
334
335 // declare a static string of that type (instantiated later at file scope)
336 static constexpr type value{};
337
338 public:
339 // pointer to the instantiated string
340 static constexpr const char * str = value.data;
341 };
342
343 // instantiate numeric_string::value as needed for different numbers
344 template<int x>
345 constexpr typename numeric_string<x>::type numeric_string<x>::value;
346
347 // Functions for creating/copying/destroying objects en masse;
348 // these will be nullptr for basic types (use memset/memcpy instead)
349 // =================================================================
350
351 typedef void (*FillFunc)(void * data, int len);
352 typedef void (*CopyFunc)(const void * from, void * to, int len);
353 typedef void (*EraseFunc)(void * data, int len);
354
355 template<class T>
356 static constexpr FillFunc fill_func()
357 {
358 return std::is_trivial<T>::value ? (FillFunc) nullptr : //
359 [](void * data, int len) {
360 T * iter = (T *)data;
361 T * end = (T *)((char *)data + len);
362 while (iter < end)
363 new (iter++) T();
364 };
365 }
366
367 template<class T>
368 static constexpr CopyFunc copy_func()
369 {
370 return std::is_trivial<T>::value ? (CopyFunc) nullptr : //
371 [](const void * from, void * to, int len) {
372 const T * src = (const T *)from;
373 T * dest = (T *)to;
374 T * end = (T *)((char *)to + len);
375 while (dest < end)
376 new (dest++) T(*src++);
377 };
378 }
379
380 template<class T>
381 static constexpr EraseFunc erase_func()
382 {
383 return std::is_trivial<T>::value ? (EraseFunc) nullptr : //
384 [](void * data, int len) {
385 T * iter = (T *)data;
386 T * end = (T *)((char *)data + len);
387 while (iter < end)
388 (*iter++).~T();
389 };
390 }
391
392 } // namespace aud
393
394 #endif // LIBAUDCORE_TEMPLATES_H
395