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