vir-simd 0.4.189
Parallelism TS 2 extensions and simd fallback implementation
Loading...
Searching...
No Matches
constexpr_wrapper.h
1/* SPDX-License-Identifier: LGPL-3.0-or-later */
2/* Copyright © 2023-2024 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH
3 * Matthias Kretz <m.kretz@gsi.de>
4 */
5
6#ifndef VIR_CONSTEXPR_WRAPPER_H_
7#define VIR_CONSTEXPR_WRAPPER_H_
8
9#if defined DOXYGEN \
10 || defined __cpp_concepts && __cpp_concepts >= 201907 && __has_include(<concepts>) \
11 && (__GNUC__ > 10 || defined __clang__)
12#define VIR_HAVE_CONSTEXPR_WRAPPER 1
13#include <algorithm>
14#include <array>
15#include <concepts>
16#include <limits>
17#include <type_traits>
18#include <utility>
19
20namespace vir
21{
31 template <auto Xp, typename = std::remove_cvref_t<decltype(Xp)>>
32 struct constexpr_wrapper;
33
34 // *** constexpr_value<T, U = void> ***
35 // If U is given, T must be convertible to U (`constexpr_value<int> auto` is analogous to `int`).
36 // If U is void (default), the type of the value is unconstrained.
37 template <typename Tp, typename Up = void>
38 concept constexpr_value = (std::same_as<Up, void> or std::convertible_to<Tp, Up>)
39#if defined __GNUC__ and __GNUC__ < 13
40 and not std::is_member_pointer_v<decltype(&Tp::value)>
41#endif
42 and requires { typename constexpr_wrapper<Tp::value>; };
43
45 namespace detail
46 {
47 // exposition-only
48 // Note: `not _any_constexpr_wrapper` is not equivalent to `not derived_from<T,
49 // constexpr_wrapper<T::value>>` because of SFINAE (i.e. SFINAE would be reversed)
50 template <typename Tp>
51 concept _any_constexpr_wrapper = std::derived_from<Tp, constexpr_wrapper<Tp::value>>;
52
53 // exposition-only
54 // Concept that enables declaring a single binary operator overload per operation:
55 // 1. LHS is derived from This, then RHS is either also derived from This (and there's only a
56 // single candidate) or RHS is not a constexpr_wrapper (LHS provides the only operator
57 // candidate).
58 // 2. LHS is not a constexpr_wrapper, then RHS is derived from This (RHS provides the only
59 // operator candidate).
60 template <typename Tp, typename This>
61 concept _lhs_constexpr_wrapper
62 = constexpr_value<Tp> and (std::derived_from<Tp, This> or not _any_constexpr_wrapper<Tp>);
63 }
64
65 // Prefer to use `constexpr_value<type> auto` instead of `template <typename T> void
66 // f(constexpr_wrapper<T, type> ...` to constrain the type of the constant.
67 template <auto Xp, typename Tp>
69 {
70 using value_type = Tp;
71
72 using type = constexpr_wrapper;
73
74 static constexpr value_type value{ Xp };
75
76 constexpr
77 operator value_type() const
78 { return Xp; }
79
80 // overloads the following:
81 //
82 // unary:
83 // + - ~ ! & * ++ -- ->
84 //
85 // binary:
86 // + - * / % & | ^ && || , << >> == != < <= > >= ->* = += -= *= /= %= &= |= ^= <<= >>=
87 //
88 // [] and () are overloaded for constexpr_value or not constexpr_value, the latter not
89 // wrapping the result in constexpr_wrapper
90
91 // The overload of -> is inconsistent because it cannot wrap its return value in a
92 // constexpr_wrapper. The compiler/standard requires a pointer type. However, -> can work if
93 // it unwraps. The utility is questionable though, which it why may be interesting to
94 // "dereferencing" the wrapper to its value if Tp doesn't implement operator->.
95 constexpr const auto*
96 operator->() const
97 {
98 if constexpr (requires{value.operator->();})
99 return value.operator->();
100 else
101 return std::addressof(value);
102 }
103
104 template <auto Yp = Xp>
105 constexpr constexpr_wrapper<+Yp>
106 operator+() const
107 { return {}; }
108
109 template <auto Yp = Xp>
110 constexpr constexpr_wrapper<-Yp>
111 operator-() const
112 { return {}; }
113
114 template <auto Yp = Xp>
115 constexpr constexpr_wrapper<~Yp>
116 operator~() const
117 { return {}; }
118
119 template <auto Yp = Xp>
120 constexpr constexpr_wrapper<!Yp>
121 operator!() const
122 { return {}; }
123
124 template <auto Yp = Xp>
125 constexpr constexpr_wrapper<&Yp>
126 operator&() const
127 { return {}; }
128
129 template <auto Yp = Xp>
130 constexpr constexpr_wrapper<*Yp>
131 operator*() const
132 { return {}; }
133
134 template <auto Yp = Xp>
136 operator++() const
137 { return {}; }
138
139 template <auto Yp = Xp>
141 operator++(int) const
142 { return {}; }
143
144 template <auto Yp = Xp>
145 constexpr constexpr_wrapper<--Yp>
146 operator--() const
147 { return {}; }
148
149 template <auto Yp = Xp>
150 constexpr constexpr_wrapper<Yp-->
151 operator--(int) const
152 { return {}; }
153
154 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
156 operator+(Ap, Bp)
157 { return {}; }
158
159 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
160 friend constexpr constexpr_wrapper<Ap::value - Bp::value>
161 operator-(Ap, Bp)
162 { return {}; }
163
164 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
166 operator*(Ap, Bp)
167 { return {}; }
168
169 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
170 friend constexpr constexpr_wrapper<Ap::value / Bp::value>
171 operator/(Ap, Bp)
172 { return {}; }
173
174 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
176 operator%(Ap, Bp)
177 { return {}; }
178
179 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
181 operator&(Ap, Bp)
182 { return {}; }
183
184 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
186 operator|(Ap, Bp)
187 { return {}; }
188
189 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
191 operator^(Ap, Bp)
192 { return {}; }
193
194 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
196 operator&&(Ap, Bp)
197 { return {}; }
198
199 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
201 operator||(Ap, Bp)
202 { return {}; }
203
204 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
205 friend constexpr constexpr_wrapper<(Ap::value , Bp::value)>
206 operator,(Ap, Bp)
207 { return {}; }
208
209 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
210 friend constexpr constexpr_wrapper<(Ap::value << Bp::value)>
211 operator<<(Ap, Bp)
212 { return {}; }
213
214 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
215 friend constexpr constexpr_wrapper<(Ap::value >> Bp::value)>
216 operator>>(Ap, Bp)
217 { return {}; }
218
219 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
220 friend constexpr constexpr_wrapper<(Ap::value == Bp::value)>
221 operator==(Ap, Bp)
222 { return {}; }
223
224 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
225 friend constexpr constexpr_wrapper<(Ap::value != Bp::value)>
226 operator!=(Ap, Bp)
227 { return {}; }
228
229 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
230 friend constexpr constexpr_wrapper<(Ap::value < Bp::value)>
231 operator<(Ap, Bp)
232 { return {}; }
233
234 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
235 friend constexpr constexpr_wrapper<(Ap::value <= Bp::value)>
236 operator<=(Ap, Bp)
237 { return {}; }
238
239 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
240 friend constexpr constexpr_wrapper<(Ap::value > Bp::value)>
241 operator>(Ap, Bp)
242 { return {}; }
243
244 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
245 friend constexpr constexpr_wrapper<(Ap::value >= Bp::value)>
246 operator>=(Ap, Bp)
247 { return {}; }
248
249 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
250 friend constexpr constexpr_wrapper<(Ap::value <=> Bp::value)>
251 operator<=>(Ap, Bp)
252 { return {}; }
253
254 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
255 friend constexpr constexpr_wrapper<(Ap::value ->* Bp::value)>
256 operator->*(Ap, Bp)
257 { return {}; }
258
259 template <constexpr_value Ap>
260 requires requires { typename constexpr_wrapper<(Xp = Ap::value)>; }
261 constexpr auto
262 operator=(Ap) const
263 { return constexpr_wrapper<(Xp = Ap::value)>{}; }
264
265 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
266 friend constexpr constexpr_wrapper<(Ap::value += Bp::value)>
267 operator+=(Ap, Bp)
268 { return {}; }
269
270 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
271 friend constexpr constexpr_wrapper<(Ap::value -= Bp::value)>
272 operator-=(Ap, Bp)
273 { return {}; }
274
275 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
276 friend constexpr constexpr_wrapper<(Ap::value *= Bp::value)>
277 operator*=(Ap, Bp)
278 { return {}; }
279
280 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
281 friend constexpr constexpr_wrapper<(Ap::value /= Bp::value)>
282 operator/=(Ap, Bp)
283 { return {}; }
284
285 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
286 friend constexpr constexpr_wrapper<(Ap::value %= Bp::value)>
287 operator%=(Ap, Bp)
288 { return {}; }
289
290 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
291 friend constexpr constexpr_wrapper<(Ap::value &= Bp::value)>
292 operator&=(Ap, Bp)
293 { return {}; }
294
295 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
296 friend constexpr constexpr_wrapper<(Ap::value |= Bp::value)>
297 operator|=(Ap, Bp)
298 { return {}; }
299
300 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
301 friend constexpr constexpr_wrapper<(Ap::value ^= Bp::value)>
302 operator^=(Ap, Bp)
303 { return {}; }
304
305 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
306 friend constexpr constexpr_wrapper<(Ap::value <<= Bp::value)>
307 operator<<=(Ap, Bp)
308 { return {}; }
309
310 template <detail::_lhs_constexpr_wrapper<constexpr_wrapper> Ap, constexpr_value Bp>
311 friend constexpr constexpr_wrapper<(Ap::value >>= Bp::value)>
312 operator>>=(Ap, Bp)
313 { return {}; }
314
315#if defined __cpp_static_call_operator && __cplusplus >= 202302L
316#define _static static
317#define _const
318#else
319#define _static
320#define _const const
321#endif
322
323 // overload operator() for constexpr_value and non-constexpr_value
324 template <constexpr_value... Args>
325 requires requires { typename constexpr_wrapper<value(Args::value...)>; }
326 _static constexpr auto
327 operator()(Args...) _const
328 { return constexpr_wrapper<value(Args::value...)>{}; }
329
330 template <typename... Args>
331 requires (not constexpr_value<std::remove_cvref_t<Args>> || ...)
332 _static constexpr decltype(value(std::declval<Args>()...))
333 operator()(Args&&... _args) _const
334 { return value(std::forward<Args>(_args)...); }
335
336 _static constexpr Tp
337 operator()() _const
338 requires (not requires { value(); })
339 { return value; }
340
341 // overload operator[] for constexpr_value and non-constexpr_value
342#if __cpp_multidimensional_subscript
343 template <constexpr_value... Args>
344 requires std::is_compound_v<value_type>
345 _static constexpr constexpr_wrapper<value[Args::value...]>
346 operator[](Args...) _const
347 { return {}; }
348
349 template <typename... Args>
350 requires (not constexpr_value<std::remove_cvref_t<Args>> || ...)
351 && std::is_compound_v<value_type>
352 _static constexpr decltype(value[std::declval<Args>()...])
353 operator[](Args&&... _args) _const
354 { return value[std::forward<Args>(_args)...]; }
355#else
356 template <constexpr_value Arg>
357 requires std::is_compound_v<value_type>
359 operator[](Arg) _const
360 { return {}; }
361
362 template <typename Arg>
363 requires (not constexpr_value<std::remove_cvref_t<Arg>>)
364 && std::is_compound_v<value_type>
365 _static constexpr decltype(value[std::declval<Arg>()])
366 operator[](Arg&& _arg) _const
367 { return value[std::forward<Arg>(_arg)]; }
368#endif
369
370#undef _static
371#undef _const
372 };
373
374 // constexpr_wrapper constant (cw)
375 template <auto Xp>
376 inline constexpr constexpr_wrapper<Xp> cw{};
377
378 namespace detail
379 {
380 template <char... Chars>
381 consteval auto
382 cw_prepare_array()
383 {
384 constexpr auto not_digit_sep = [](char c) { return c != '\''; };
385 constexpr char arr0[sizeof...(Chars)] = {Chars...};
386 constexpr auto size
387 = std::count_if(std::begin(arr0), std::end(arr0), not_digit_sep);
388 std::array<char, size> tmp = {};
389 std::copy_if(std::begin(arr0), std::end(arr0), tmp.begin(), not_digit_sep);
390 return tmp;
391 }
392
393 template <char... Chars>
394 consteval auto
395 cw_parse()
396 {
397 constexpr std::array arr = cw_prepare_array<Chars...>();
398 constexpr int base = arr[0] == '0' and 2 < arr.size()
399 ? arr[1] == 'x' or arr[1] == 'X' ? 16
400 : arr[1] == 'b' ? 2 : 8
401 : 10;
402 constexpr int offset = base == 10 ? 0 : base == 8 ? 1 : 2;
403 constexpr bool valid_chars = std::all_of(arr.begin() + offset, arr.end(), [=](char c) {
404 if constexpr (base == 16)
405 return (c >= 'a' and c <= 'f') or (c >= 'A' and c <= 'F')
406 or (c >= '0' and c <= '9');
407 else
408 return c >= '0' and c < char('0' + base);
409 });
410 static_assert(valid_chars, "invalid characters in constexpr_wrapper literal");
411
412 // common values, freeing values for error conditions
413 if constexpr (arr.size() == 1 and arr[0] =='0')
414 return static_cast<signed char>(0);
415 else if constexpr (arr.size() == 1 and arr[0] =='1')
416 return static_cast<signed char>(1);
417 else if constexpr (arr.size() == 1 and arr[0] =='2')
418 return static_cast<signed char>(2);
419 else
420 {
421 constexpr unsigned long long x = [&]() {
422 unsigned long long x = {};
423 constexpr auto max = std::numeric_limits<unsigned long long>::max();
424 auto it = arr.begin() + offset;
425 for (; it != arr.end(); ++it)
426 {
427 unsigned nextdigit = *it - '0';
428 if constexpr (base == 16)
429 {
430 if (*it >= 'a')
431 nextdigit = *it - 'a' + 10;
432 else if (*it >= 'A')
433 nextdigit = *it - 'A' + 10;
434 }
435 if (x > max / base)
436 return 0ull;
437 x *= base;
438 if (x > max - nextdigit)
439 return 0ull;
440 x += nextdigit;
441 }
442 return x;
443 }();
444 static_assert(x != 0, "constexpr_wrapper literal value out of range");
445 if constexpr (x <= std::numeric_limits<signed char>::max())
446 return static_cast<signed char>(x);
447 else if constexpr (x <= std::numeric_limits<signed short>::max())
448 return static_cast<signed short>(x);
449 else if constexpr (x <= std::numeric_limits<signed int>::max())
450 return static_cast<signed int>(x);
451 else if constexpr (x <= std::numeric_limits<signed long>::max())
452 return static_cast<signed long>(x);
453 else if constexpr (x <= std::numeric_limits<signed long long>::max())
454 return static_cast<signed long long>(x);
455 else
456 return x;
457 }
458 }
459 }
460
461 namespace literals
462 {
463 template <char... Chars>
464 constexpr auto operator""_cw()
465 { return vir::cw<vir::detail::cw_parse<Chars...>()>; }
466 }
467
468} // namespace vir
469
470#endif // has concepts
471#endif // VIR_CONSTEXPR_WRAPPER_H_
472// vim: et tw=100 ts=8 sw=2 cc=101
This namespace collects libraries and tools authored by Matthias Kretz.
Definition constexpr_wrapper.h:21
Definition constexpr_wrapper.h:69