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