gw  1.0.0
A bunch of small C++ utilities
named_type.hpp
1 // Copyright (c) 2023 Martin Stump
2 // SPDX-License-Identifier: BSL-1.0
3 
4 #pragma once
5 
6 #include <algorithm>
7 #include <compare>
8 #include <concepts>
9 #include <cstddef>
10 #include <functional>
11 #include <initializer_list>
12 #include <iostream>
13 #include <ranges>
14 #include <string>
15 #include <string_view>
16 #include <type_traits>
17 #include <utility>
18 
19 #include "gw/concepts.hpp"
20 
22 namespace gw {
23 
24 namespace detail {
25 
26 template <std::size_t N>
27 struct fixed_string {
28  // NOLINTNEXTLINE
29  constexpr fixed_string(const char (&str)[N]) { std::copy_n(str, N, value); }
30 
31  // NOLINTNEXTLINE
32  char value[N]{};
33 };
34 
35 struct named_type_empty_base {
36  constexpr auto operator<=>(const named_type_empty_base&) const noexcept = default;
37 };
38 
39 } // namespace detail
40 
42 //
44 //
51 //
52 template <typename T, detail::fixed_string Name>
53 class named_type final
54  : public std::conditional_t<std::ranges::range<T>, std::ranges::view_interface<named_type<T, Name>>,
55  detail::named_type_empty_base> {
56  public:
57  //
58  // Public types
59  //
60 
62  using value_type = T;
63 
64  //
65  // Constructors
66  //
67 
69  template <typename... Args>
70  constexpr explicit named_type(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
71  requires std::constructible_from<T, Args...>
72  : m_value(T{std::forward<Args>(args)...}) {}
73 
75  template <typename U, typename... Args>
76  constexpr named_type(std::initializer_list<U> ilist,
77  Args&&... args) noexcept(std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>)
78  requires std::constructible_from<T, std::initializer_list<U>&, Args...>
79  : m_value(T{ilist, std::forward<Args>(args)...}) {}
80 
81  //
82  // Destructor
83  //
84 
86  ~named_type() noexcept(std::is_nothrow_destructible_v<T>) = default;
87 
88  //
89  // Static functions
90  //
91 
93  static constexpr auto name() noexcept -> std::string_view { return k_name; }
94 
95  //
96  // Observers
97  //
98 
100  constexpr auto operator->() const noexcept -> const T* { return &m_value; }
101 
103  constexpr auto operator->() noexcept -> T* { return &m_value; }
104 
106  constexpr auto operator*() const& noexcept -> const T& { return m_value; }
107 
109  constexpr auto operator*() & noexcept -> T& { return m_value; }
110 
112  constexpr auto operator*() const&& noexcept -> const T&& { return std::move(m_value); }
113 
115  constexpr auto operator*() && noexcept -> T&& { return std::move(m_value); }
116 
118  constexpr auto value() const& noexcept -> const T& { return m_value; }
119 
121  constexpr auto value() & noexcept -> T& { return m_value; }
122 
124  constexpr auto value() const&& noexcept -> const T&& { return std::move(m_value); }
125 
127  constexpr auto value() && noexcept -> T&& { return std::move(m_value); }
128 
129  //
130  // Monadic operations
131  //
132 
134  template <typename F>
135  constexpr auto transform(F&& func) const& noexcept(noexcept(func(m_value)))
136  requires std::invocable<F, const T&>
137  {
139  }
140 
142  template <typename F>
143  constexpr auto transform(F&& func) & noexcept(noexcept(func(m_value)))
144  requires std::invocable<F, T&>
145  {
146  return named_type<std::remove_cv_t<std::invoke_result_t<F, T&>>, Name>{func(m_value)};
147  }
148 
150  template <typename F>
151  constexpr auto transform(F&& func) const&& noexcept(noexcept(func(m_value)))
152  requires std::invocable<F, const T&&>
153  {
154  return named_type<std::remove_cv_t<std::invoke_result_t<F, const T&&>>, Name>{func(std::move(m_value))};
155  }
156 
158  template <typename F>
159  constexpr auto transform(F&& func) && noexcept(noexcept(func(m_value)))
160  requires std::invocable<F, T&&>
161  {
162  return named_type<std::remove_cv_t<std::invoke_result_t<F, T&&>>, Name>{func(std::move(m_value))};
163  }
164 
165  //
166  // Modifiers
167  //
168 
170  constexpr void swap(named_type& rhs) noexcept(std::is_nothrow_swappable_v<T>)
171  requires std::swappable<T>
172  {
173  using std::swap;
174  swap(m_value, rhs.m_value);
175  }
176 
178  constexpr void reset() noexcept(std::is_nothrow_default_constructible_v<T>)
179  requires std::default_initializable<T>
180  {
181  m_value = T{};
182  }
183 
185  template <typename... Args>
186  constexpr auto emplace(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>) -> T&
187  requires std::constructible_from<T, Args...>
188  {
189  m_value = T{std::forward<Args>(args)...};
190  return m_value;
191  }
192 
193  //
194  // Comparison operators
195  //
196 
198  constexpr auto operator==(const named_type& rhs) const& noexcept(noexcept(m_value == rhs.m_value)) -> bool
199  requires std::equality_comparable<T>
200  {
201  return m_value == rhs.m_value;
202  }
203 
205  constexpr auto operator!=(const named_type& rhs) const& noexcept(noexcept(m_value != rhs.m_value)) -> bool
206  requires std::equality_comparable<T>
207  {
208  return m_value != rhs.m_value;
209  }
210 
212  constexpr auto operator<(const named_type& rhs) const& noexcept(noexcept(m_value < rhs.m_value)) -> bool
213  requires std::totally_ordered<T>
214  {
215  return m_value < rhs.m_value;
216  }
217 
219  constexpr auto operator>(const named_type& rhs) const& noexcept(noexcept(m_value > rhs.m_value)) -> bool
220  requires std::totally_ordered<T>
221  {
222  return m_value > rhs.m_value;
223  }
224 
226  constexpr auto operator<=(const named_type& rhs) const& noexcept(noexcept(m_value <= rhs.m_value)) -> bool
227  requires std::totally_ordered<T>
228  {
229  return m_value <= rhs.m_value;
230  }
231 
233  constexpr auto operator>=(const named_type& rhs) const& noexcept(noexcept(m_value >= rhs.m_value)) -> bool
234  requires std::totally_ordered<T>
235  {
236  return m_value >= rhs.m_value;
237  }
238 
240  constexpr auto operator<=>(const named_type& rhs) const& noexcept(noexcept(m_value <=>
241  rhs.m_value)) -> std::strong_ordering
242  requires std::three_way_comparable<T>
243  {
244  return m_value <=> rhs.m_value;
245  }
246 
247  //
248  // Conversion operators
249  //
250 
252  constexpr explicit operator const T&() const& noexcept { return m_value; }
253 
255  constexpr explicit operator T&() & noexcept { return m_value; }
256 
258  constexpr explicit operator const T&&() const&& noexcept { return std::move(m_value); }
259 
261  constexpr explicit operator T&&() && noexcept { return std::move(m_value); }
262 
263  //
264  // Increment and decrement operators
265  //
266 
268  constexpr auto operator++() & noexcept(noexcept(++m_value)) -> named_type&
269  requires incrementable<T>
270  {
271  ++m_value;
272  return *this;
273  }
274 
276  constexpr auto operator++() && noexcept(noexcept(++m_value)) -> named_type&&
277  requires incrementable<T>
278  {
279  ++m_value;
280  return std::move(*this);
281  }
282 
284  constexpr auto operator++(int) & noexcept(noexcept(m_value++)) -> named_type
285  requires incrementable<T>
286  {
287  return named_type{m_value++};
288  }
289 
291  constexpr auto operator++(int) && noexcept(noexcept(m_value++)) -> named_type
292  requires incrementable<T>
293  {
294  return named_type{m_value++};
295  }
296 
298  constexpr auto operator--() & noexcept(noexcept(--m_value)) -> named_type&
299  requires decrementable<T>
300  {
301  --m_value;
302  return *this;
303  }
304 
306  constexpr auto operator--() && noexcept(noexcept(--m_value)) -> named_type&&
307  requires decrementable<T>
308  {
309  --m_value;
310  return std::move(*this);
311  }
312 
314  constexpr auto operator--(int) & noexcept(noexcept(m_value--)) -> named_type
315  requires decrementable<T>
316  {
317  return named_type{m_value--};
318  }
319 
321  constexpr auto operator--(int) && noexcept(noexcept(m_value--)) -> named_type
322  requires decrementable<T>
323  {
324  return named_type{m_value--};
325  }
326 
327  //
328  // Arithmetic operators
329  //
330 
332  constexpr auto operator+() const& noexcept(noexcept(+m_value)) -> named_type
333  requires std::signed_integral<T>
334  {
335  return named_type{+m_value};
336  }
337 
339  constexpr auto operator+() && noexcept(noexcept(+m_value)) -> named_type
340  requires std::signed_integral<T>
341  {
342  return named_type{+m_value};
343  }
344 
346  constexpr auto operator-() const& noexcept(noexcept(-m_value)) -> named_type
347  requires std::signed_integral<T>
348  {
349  return named_type{-m_value};
350  }
351 
353  constexpr auto operator-() && noexcept(noexcept(-m_value)) -> named_type
354  requires std::signed_integral<T>
355  {
356  return named_type{-m_value};
357  }
358 
360  constexpr auto operator+(const named_type& rhs) const& noexcept(noexcept(m_value + rhs.m_value)) -> named_type
361  requires arithmetic<T>
362  {
363  return named_type{m_value + rhs.m_value};
364  }
365 
367  constexpr auto operator+(named_type&& rhs) const& noexcept(noexcept(m_value + rhs.m_value)) -> named_type
368  requires arithmetic<T>
369  {
370  return named_type{m_value + rhs.m_value};
371  }
372 
374  constexpr auto operator+(const named_type& rhs) && noexcept(noexcept(m_value + rhs.m_value)) -> named_type
375  requires arithmetic<T>
376  {
377  return named_type{m_value + rhs.m_value};
378  }
379 
381  constexpr auto operator+(named_type&& rhs) && noexcept(noexcept(m_value + rhs.m_value)) -> named_type
382  requires arithmetic<T>
383  {
384  return named_type{m_value + rhs.m_value};
385  }
386 
388  constexpr auto operator-(const named_type& rhs) const& noexcept(noexcept(m_value - rhs.m_value)) -> named_type
389  requires arithmetic<T>
390  {
391  return named_type{m_value - rhs.m_value};
392  }
393 
395  constexpr auto operator-(named_type&& rhs) const& noexcept(noexcept(m_value - rhs.m_value)) -> named_type
396  requires arithmetic<T>
397  {
398  return named_type{m_value - rhs.m_value};
399  }
400 
402  constexpr auto operator-(const named_type& rhs) && noexcept(noexcept(m_value - rhs.m_value)) -> named_type
403  requires arithmetic<T>
404  {
405  return named_type{m_value - rhs.m_value};
406  }
407 
409  constexpr auto operator-(named_type&& rhs) && noexcept(noexcept(m_value - rhs.m_value)) -> named_type
410  requires arithmetic<T>
411  {
412  return named_type{m_value - rhs.m_value};
413  }
414 
416  constexpr auto operator*(const named_type& rhs) const& noexcept(noexcept(m_value * rhs.m_value)) -> named_type
417  requires arithmetic<T>
418  {
419  return named_type{m_value * rhs.m_value};
420  }
421 
423  constexpr auto operator*(named_type&& rhs) const& noexcept(noexcept(m_value * rhs.m_value)) -> named_type
424  requires arithmetic<T>
425  {
426  return named_type{m_value * rhs.m_value};
427  }
428 
430  constexpr auto operator*(const named_type& rhs) && noexcept(noexcept(m_value * rhs.m_value)) -> named_type
431  requires arithmetic<T>
432  {
433  return named_type{m_value * rhs.m_value};
434  }
435 
437  constexpr auto operator*(named_type&& rhs) && noexcept(noexcept(m_value * rhs.m_value)) -> named_type
438  requires arithmetic<T>
439  {
440  return named_type{m_value * rhs.m_value};
441  }
442 
444  constexpr auto operator/(const named_type& rhs) const& noexcept(noexcept(m_value / rhs.m_value)) -> named_type
445  requires arithmetic<T>
446  {
447  return named_type{m_value / rhs.m_value};
448  }
449 
451  constexpr auto operator/(named_type&& rhs) const& noexcept(noexcept(m_value / rhs.m_value)) -> named_type
452  requires arithmetic<T>
453  {
454  return named_type{m_value / rhs.m_value};
455  }
456 
458  constexpr auto operator/(const named_type& rhs) && noexcept(noexcept(m_value / rhs.m_value)) -> named_type
459  requires arithmetic<T>
460  {
461  return named_type{m_value / rhs.m_value};
462  }
463 
465  constexpr auto operator/(named_type&& rhs) && noexcept(noexcept(m_value / rhs.m_value)) -> named_type
466  requires arithmetic<T>
467  {
468  return named_type{m_value / rhs.m_value};
469  }
470 
472  constexpr auto operator%(const named_type& rhs) const& noexcept(noexcept(m_value % rhs.m_value)) -> named_type
473  requires arithmetic<T>
474  {
475  return named_type{m_value % rhs.m_value};
476  }
477 
479  constexpr auto operator%(named_type&& rhs) const& noexcept(noexcept(m_value % rhs.m_value)) -> named_type
480  requires arithmetic<T>
481  {
482  return named_type{m_value % rhs.m_value};
483  }
484 
486  constexpr auto operator%(const named_type& rhs) && noexcept(noexcept(m_value % rhs.m_value)) -> named_type
487  requires arithmetic<T>
488  {
489  return named_type{m_value % rhs.m_value};
490  }
491 
493  constexpr auto operator%(named_type&& rhs) && noexcept(noexcept(m_value % rhs.m_value)) -> named_type
494  requires arithmetic<T>
495  {
496  return named_type{m_value % rhs.m_value};
497  }
498 
500  constexpr auto operator+=(const named_type& rhs) & noexcept(noexcept(m_value += rhs.m_value)) -> named_type&
501  requires arithmetic<T>
502  {
503  m_value += rhs.m_value;
504  return *this;
505  }
506 
508  constexpr auto operator+=(named_type&& rhs) & noexcept(noexcept(m_value += rhs.m_value)) -> named_type&
509  requires arithmetic<T>
510  {
511  m_value += rhs.m_value;
512  return *this;
513  }
514 
516  constexpr auto operator-=(const named_type& rhs) & noexcept(noexcept(m_value -= rhs.m_value)) -> named_type&
517  requires arithmetic<T>
518  {
519  m_value -= rhs.m_value;
520  return *this;
521  }
522 
524  constexpr auto operator-=(named_type&& rhs) & noexcept(noexcept(m_value -= rhs.m_value)) -> named_type&
525  requires arithmetic<T>
526  {
527  m_value -= rhs.m_value;
528  return *this;
529  }
530 
532  constexpr auto operator*=(const named_type& rhs) & noexcept(noexcept(m_value *= rhs.m_value)) -> named_type&
533  requires arithmetic<T>
534  {
535  m_value *= rhs.m_value;
536  return *this;
537  }
538 
540  constexpr auto operator*=(named_type&& rhs) & noexcept(noexcept(m_value *= rhs.m_value)) -> named_type&
541  requires arithmetic<T>
542  {
543  m_value *= rhs.m_value;
544  return *this;
545  }
546 
548  constexpr auto operator/=(const named_type& rhs) & noexcept(noexcept(m_value /= rhs.m_value)) -> named_type&
549  requires arithmetic<T>
550  {
551  m_value /= rhs.m_value;
552  return *this;
553  }
554 
556  constexpr auto operator/=(named_type&& rhs) & noexcept(noexcept(m_value /= rhs.m_value)) -> named_type&
557  requires arithmetic<T>
558  {
559  m_value /= rhs.m_value;
560  return *this;
561  }
562 
564  constexpr auto operator%=(const named_type& rhs) & noexcept(noexcept(m_value %= rhs.m_value)) -> named_type&
565  requires arithmetic<T>
566  {
567  m_value %= rhs.m_value;
568  return *this;
569  }
570 
572  constexpr auto operator%=(named_type&& rhs) & noexcept(noexcept(m_value %= rhs.m_value)) -> named_type&
573  requires arithmetic<T>
574  {
575  m_value %= rhs.m_value;
576  return *this;
577  }
578 
579  //
580  // Bitwise operators
581  //
582 
584  constexpr auto operator~() const& noexcept(noexcept(~m_value)) -> named_type
585  requires std::unsigned_integral<T>
586  {
587  return named_type{~m_value};
588  }
589 
591  constexpr auto operator&(const named_type& rhs) const& noexcept(noexcept(m_value & rhs.m_value)) -> named_type
592  requires std::unsigned_integral<T>
593  {
594  return named_type{m_value & rhs.m_value};
595  }
596 
598  constexpr auto operator&(named_type&& rhs) const& noexcept(noexcept(m_value & rhs.m_value)) -> named_type
599  requires std::unsigned_integral<T>
600  {
601  return named_type{m_value & rhs.m_value};
602  }
603 
605  constexpr auto operator&(const named_type& rhs) && noexcept(noexcept(m_value & rhs.m_value)) -> named_type
606  requires std::unsigned_integral<T>
607  {
608  return named_type{m_value & rhs.m_value};
609  }
610 
612  constexpr auto operator&(named_type&& rhs) && noexcept(noexcept(m_value & rhs.m_value)) -> named_type
613  requires std::unsigned_integral<T>
614  {
615  return named_type{m_value & rhs.m_value};
616  }
617 
619  constexpr auto operator|(const named_type& rhs) const& noexcept(noexcept(m_value | rhs.m_value)) -> named_type
620  requires std::unsigned_integral<T>
621  {
622  return named_type{m_value | rhs.m_value};
623  }
624 
626  constexpr auto operator|(named_type&& rhs) const& noexcept(noexcept(m_value | rhs.m_value)) -> named_type
627  requires std::unsigned_integral<T>
628  {
629  return named_type{m_value | rhs.m_value};
630  }
631 
633  constexpr auto operator|(const named_type& rhs) && noexcept(noexcept(m_value | rhs.m_value)) -> named_type
634  requires std::unsigned_integral<T>
635  {
636  return named_type{m_value | rhs.m_value};
637  }
638 
640  constexpr auto operator|(named_type&& rhs) && noexcept(noexcept(m_value | rhs.m_value)) -> named_type
641  requires std::unsigned_integral<T>
642  {
643  return named_type{m_value | rhs.m_value};
644  }
645 
647  constexpr auto operator^(const named_type& rhs) const& noexcept(noexcept(m_value ^ rhs.m_value)) -> named_type
648  requires std::unsigned_integral<T>
649  {
650  return named_type{m_value ^ rhs.m_value};
651  }
652 
654  constexpr auto operator^(named_type&& rhs) const& noexcept(noexcept(m_value ^ rhs.m_value)) -> named_type
655  requires std::unsigned_integral<T>
656  {
657  return named_type{m_value ^ rhs.m_value};
658  }
659 
661  constexpr auto operator^(const named_type& rhs) && noexcept(noexcept(m_value ^ rhs.m_value)) -> named_type
662  requires std::unsigned_integral<T>
663  {
664  return named_type{m_value ^ rhs.m_value};
665  }
666 
668  constexpr auto operator^(named_type&& rhs) && noexcept(noexcept(m_value ^ rhs.m_value)) -> named_type
669  requires std::unsigned_integral<T>
670  {
671  return named_type{m_value ^ rhs.m_value};
672  }
673 
675  constexpr auto operator<<(const named_type& rhs) const& noexcept(noexcept(m_value << rhs.m_value)) -> named_type
676  requires std::unsigned_integral<T>
677  {
678  return named_type{m_value << rhs.m_value};
679  }
680 
682  constexpr auto operator<<(named_type&& rhs) const& noexcept(noexcept(m_value << rhs.m_value)) -> named_type
683  requires std::unsigned_integral<T>
684  {
685  return named_type{m_value << rhs.m_value};
686  }
687 
689  constexpr auto operator<<(const named_type& rhs) && noexcept(noexcept(m_value << rhs.m_value)) -> named_type
690  requires std::unsigned_integral<T>
691  {
692  return named_type{m_value << rhs.m_value};
693  }
694 
696  constexpr auto operator<<(named_type&& rhs) && noexcept(noexcept(m_value << rhs.m_value)) -> named_type
697  requires std::unsigned_integral<T>
698  {
699  return named_type{m_value << rhs.m_value};
700  }
701 
703  constexpr auto operator>>(const named_type& rhs) const& noexcept(noexcept(m_value >> rhs.m_value)) -> named_type
704  requires std::unsigned_integral<T>
705  {
706  return named_type{m_value >> rhs.m_value};
707  }
708 
710  constexpr auto operator>>(named_type&& rhs) const& noexcept(noexcept(m_value >> rhs.m_value)) -> named_type
711  requires std::unsigned_integral<T>
712  {
713  return named_type{m_value >> rhs.m_value};
714  }
715 
717  constexpr auto operator>>(const named_type& rhs) && noexcept(noexcept(m_value >> rhs.m_value)) -> named_type
718  requires std::unsigned_integral<T>
719  {
720  return named_type{m_value >> rhs.m_value};
721  }
722 
724  constexpr auto operator>>(named_type&& rhs) && noexcept(noexcept(m_value >> rhs.m_value)) -> named_type
725  requires std::unsigned_integral<T>
726  {
727  return named_type{m_value >> rhs.m_value};
728  }
729 
731  constexpr auto operator&=(const named_type& rhs) & noexcept(noexcept(m_value &= rhs.m_value)) -> named_type&
732  requires std::unsigned_integral<T>
733  {
734  m_value &= rhs.m_value;
735  return *this;
736  }
737 
739  constexpr auto operator&=(named_type&& rhs) & noexcept(noexcept(m_value &= rhs.m_value)) -> named_type&
740  requires std::unsigned_integral<T>
741  {
742  m_value &= rhs.m_value;
743  return *this;
744  }
745 
747  constexpr auto operator|=(const named_type& rhs) & noexcept(noexcept(m_value |= rhs.m_value)) -> named_type&
748  requires std::unsigned_integral<T>
749  {
750  m_value |= rhs.m_value;
751  return *this;
752  }
753 
755  constexpr auto operator|=(named_type&& rhs) & noexcept(noexcept(m_value |= rhs.m_value)) -> named_type&
756  requires std::unsigned_integral<T>
757  {
758  m_value |= rhs.m_value;
759  return *this;
760  }
761 
763  constexpr auto operator^=(const named_type& rhs) & noexcept(noexcept(m_value ^= rhs.m_value)) -> named_type&
764  requires std::unsigned_integral<T>
765  {
766  m_value ^= rhs.m_value;
767  return *this;
768  }
769 
771  constexpr auto operator^=(named_type&& rhs) & noexcept(noexcept(m_value ^= rhs.m_value)) -> named_type&
772  requires std::unsigned_integral<T>
773  {
774  m_value ^= rhs.m_value;
775  return *this;
776  }
777 
779  constexpr auto operator<<=(const named_type& rhs) & noexcept(noexcept(m_value <<= rhs.m_value)) -> named_type&
780  requires std::unsigned_integral<T>
781  {
782  m_value <<= rhs.m_value;
783  return *this;
784  }
785 
787  constexpr auto operator<<=(named_type&& rhs) & noexcept(noexcept(m_value <<= rhs.m_value)) -> named_type&
788  requires std::unsigned_integral<T>
789  {
790  m_value <<= rhs.m_value;
791  return *this;
792  }
793 
795  constexpr auto operator>>=(const named_type& rhs) & noexcept(noexcept(m_value >>= rhs.m_value)) -> named_type&
796  requires std::unsigned_integral<T>
797  {
798  m_value >>= rhs.m_value;
799  return *this;
800  }
801 
803  constexpr auto operator>>=(named_type&& rhs) & noexcept(noexcept(m_value >>= rhs.m_value)) -> named_type&
804  requires std::unsigned_integral<T>
805  {
806  m_value >>= rhs.m_value;
807  return *this;
808  }
809 
810  //
811  // Ranges interface
812  //
813 
815  constexpr auto begin() const noexcept(noexcept(std::ranges::begin(m_value)))
816  requires std::ranges::range<T>
817  {
818  return std::ranges::begin(m_value);
819  }
820 
822  constexpr auto begin() noexcept(noexcept(std::ranges::begin(m_value)))
823  requires std::ranges::range<T>
824  {
825  return std::ranges::begin(m_value);
826  }
827 
829  constexpr auto end() const noexcept(noexcept(std::ranges::end(m_value)))
830  requires std::ranges::range<T>
831  {
832  return std::ranges::end(m_value);
833  }
834 
836  constexpr auto end() noexcept(noexcept(std::ranges::end(m_value)))
837  requires std::ranges::range<T>
838  {
839  return std::ranges::end(m_value);
840  }
841 
842  //
843  // Stream operators
844  //
845 
847  friend inline auto operator<<(std::ostream& ostream,
848  const named_type& rhs) noexcept(noexcept(ostream << rhs.m_value)) -> std::ostream&
849  requires ostreamable<T>
850  {
851  return ostream << rhs.m_value;
852  }
853 
855  friend inline auto operator>>(std::istream& istream,
856  named_type& rhs) noexcept(noexcept(istream >> rhs.m_value)) -> std::istream&
857  requires istreamable<T>
858  {
859  return istream >> rhs.m_value;
860  }
861 
862  private:
863  T m_value{};
864  static constexpr auto k_name = Name.value;
865 };
866 
867 //
868 // Creation functions
869 //
870 
872 template <detail::fixed_string Name, typename T, typename... Args>
873 constexpr auto make_named_type(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
874  requires std::constructible_from<std::remove_cvref_t<T>, Args...>
875 {
876  return named_type<std::remove_cvref_t<T>, Name>{std::forward<Args>(args)...};
877 }
878 
880 template <detail::fixed_string Name, typename T, typename U, typename... Args>
881 constexpr auto make_named_type(std::initializer_list<U> ilist, Args&&... args) noexcept(
882  std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>)
883  requires std::constructible_from<T, std::initializer_list<U>&, Args...>
884 {
885  return named_type<std::remove_cvref_t<T>, Name>{ilist, std::forward<Args>(args)...};
886 }
887 
889 template <detail::fixed_string Name, typename T>
890 constexpr auto make_named_type(T&& value) noexcept(
891  std::is_nothrow_constructible_v<named_type<std::remove_cvref_t<T>, Name>, T>)
892  requires std::constructible_from<std::remove_cvref_t<T>, T>
893 {
894  return named_type<std::remove_cvref_t<T>, Name>{std::forward<T>(value)};
895 }
896 
897 } // namespace gw
898 
899 namespace std {
900 
901 //
902 // Hash calculation
903 //
904 
906 template <::gw::hashable T, ::gw::detail::fixed_string Name>
907 // NOLINTNEXTLINE(cert-dcl58-cpp)
908 struct hash<::gw::named_type<T, Name>> {
909  [[nodiscard]] auto inline operator()(const ::gw::named_type<T, Name>& named_type) const noexcept -> size_t {
910  auto value_hash = hash<T>{}(named_type.value());
911  auto name_hash = hash<string_view>{}(named_type.name());
912  return value_hash ^ name_hash;
913  }
914 };
915 
916 //
917 // String conversion
918 //
919 
921 template <::gw::string_convertable T, ::gw::detail::fixed_string Name>
922 // NOLINTNEXTLINE(cert-dcl58-cpp)
923 [[nodiscard]] auto inline to_string(const ::gw::named_type<T, Name>& named_type) -> string {
924  return string{named_type.name()} + ": " + to_string(named_type.value());
925 }
926 
927 } // namespace std
Named type wrapper.
Definition: named_type.hpp:55
constexpr auto operator*() const &noexcept -> const T &
accesses the contained value
Definition: named_type.hpp:106
T value_type
the type of the contained value
Definition: named_type.hpp:62
constexpr auto operator->() noexcept -> T *
accesses the contained value
Definition: named_type.hpp:103
constexpr auto static constexpr transform(F &&func) const &noexcept(noexcept(func(m_value))) requires std auto k_name
returns a gw::named_type containing the transformed contained value
Definition: named_type.hpp:864
constexpr auto operator*() const &&noexcept -> const T &&
accesses the contained value
Definition: named_type.hpp:112
constexpr auto operator*() &noexcept -> T &
accesses the contained value
Definition: named_type.hpp:109
~named_type() noexcept(std::is_nothrow_destructible_v< T >)=default
destroys the contained value
constexpr auto operator*() &&noexcept -> T &&
accesses the contained value
Definition: named_type.hpp:115
constexpr named_type(std::initializer_list< U > ilist, Args &&... args) noexcept(std::is_nothrow_constructible_v< T, std::initializer_list< U > &, Args... >) requires std
constructs the gw::named_type object
Definition: named_type.hpp:76
constexpr auto value() &noexcept -> T &
returns the contained value
Definition: named_type.hpp:121
constexpr auto value() const &noexcept -> const T &
returns the contained value
Definition: named_type.hpp:118
constexpr auto value() const &&noexcept -> const T &&
returns the contained value
Definition: named_type.hpp:124
static constexpr auto name() noexcept -> std::string_view
returns the name of the gw::named_type
Definition: named_type.hpp:93
constexpr auto value() &&noexcept -> T &&
returns the contained value
Definition: named_type.hpp:127
constexpr named_type(Args &&... args) noexcept(std::is_nothrow_constructible_v< T, Args... >) requires std
constructs the gw::named_type object
Definition: named_type.hpp:70
constexpr auto operator->() const noexcept -> const T *
accesses the contained value
Definition: named_type.hpp:100
GW namespace.
Definition: concepts.hpp:14