gw  1.0.0
A bunch of small C++ utilities
Loading...
Searching...
No Matches
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 <format>
10#include <functional>
11#include <initializer_list>
12#include <iostream>
13#include <ranges>
14#include <string>
15#include <type_traits>
16#include <typeindex>
17#include <utility>
18
19#include "gw/concepts.hpp"
20
22namespace gw {
23
24namespace detail {
25
26struct strong_type_empty_base {
27 constexpr auto operator<=>(const strong_type_empty_base&) const noexcept -> std::strong_ordering = default;
28};
29
30} // namespace detail
31
33//
35//
42//
43template <typename T, typename Tag>
44class strong_type final
45 : public std::conditional_t<std::ranges::range<T>, std::ranges::view_interface<strong_type<T, Tag>>,
46 detail::strong_type_empty_base> {
47 public:
48 //
49 // Public types
50 //
51
52 using value_type = T;
53 using tag_type = Tag;
54
55 //
56 // Constructors
57 //
58
60 template <typename... Args>
61 constexpr explicit strong_type(Args&&... args) noexcept(std::is_nothrow_constructible_v<value_type, Args...>)
62 requires std::constructible_from<value_type, Args...>
63 : m_value(value_type{std::forward<Args>(args)...}) {}
64
66 template <typename U, typename... Args>
67 constexpr strong_type(std::initializer_list<U> ilist, Args&&... args) noexcept(
68 std::is_nothrow_constructible_v<value_type, std::initializer_list<U>&, Args...>)
69 requires std::constructible_from<value_type, std::initializer_list<U>&, Args...>
70 : m_value(value_type{ilist, std::forward<Args>(args)...}) {}
71
72 //
73 // Destructor
74 //
75
77 ~strong_type() noexcept(std::is_nothrow_destructible_v<value_type>) = default;
78
79 //
80 // Observers
81 //
82
84 constexpr auto operator->() const noexcept -> const value_type* { return &m_value; }
85
87 constexpr auto operator->() noexcept -> value_type* { return &m_value; }
88
90 constexpr auto operator*() const& noexcept -> const value_type& { return m_value; }
91
93 constexpr auto operator*() & noexcept -> value_type& { return m_value; }
94
96 constexpr auto operator*() const&& noexcept -> const value_type&& { return std::move(m_value); }
97
99 constexpr auto operator*() && noexcept -> value_type&& { return std::move(m_value); }
100
102 constexpr auto value() const& noexcept -> const value_type& { return m_value; }
103
105 constexpr auto value() & noexcept -> value_type& { return m_value; }
106
108 constexpr auto value() const&& noexcept -> const value_type&& { return std::move(m_value); }
109
111 constexpr auto value() && noexcept -> value_type&& { return std::move(m_value); }
112
113 //
114 // Monadic operations
115 //
116
118 template <typename F>
119 constexpr auto transform(F&& func) const& noexcept(noexcept(func(m_value))) -> strong_type
120 requires std::invocable<F, const value_type&>
121 {
123 }
124
126 template <typename F>
127 constexpr auto transform(F&& func) & noexcept(noexcept(func(m_value))) -> strong_type
128 requires std::invocable<F, value_type&>
129 {
131 }
132
134 template <typename F>
135 constexpr auto transform(F&& func) const&& noexcept(noexcept(func(m_value))) -> strong_type
136 requires std::invocable<F, const value_type&&>
137 {
139 func(std::move(m_value))};
140 }
141
143 template <typename F>
144 constexpr auto transform(F&& func) && noexcept(noexcept(func(m_value))) -> strong_type
145 requires std::invocable<F, value_type&&>
146 {
148 }
149
150 //
151 // Modifiers
152 //
153
155 constexpr void swap(strong_type& rhs) noexcept(std::is_nothrow_swappable_v<value_type>)
156 requires std::swappable<value_type>
157 {
158 using std::swap;
159 swap(m_value, rhs.m_value);
160 }
161
163 constexpr void reset() noexcept(std::is_nothrow_default_constructible_v<value_type>)
164 requires std::default_initializable<value_type>
165 {
166 m_value = value_type{};
167 }
168
170 template <typename... Args>
171 constexpr auto emplace(Args&&... args) noexcept(std::is_nothrow_constructible_v<value_type, Args...>) -> value_type&
172 requires std::constructible_from<value_type, Args...>
173 {
174 m_value = value_type{std::forward<Args>(args)...};
175 return m_value;
176 }
177
178 //
179 // Comparison operators
180 //
181
183 constexpr auto operator==(const strong_type& rhs) const& noexcept(noexcept(m_value == rhs.m_value)) -> bool
184 requires std::equality_comparable<value_type>
185 {
186 return m_value == rhs.m_value;
187 }
188
190 constexpr auto operator!=(const strong_type& rhs) const& noexcept(noexcept(m_value != rhs.m_value)) -> bool
191 requires std::equality_comparable<value_type>
192 {
193 return m_value != rhs.m_value;
194 }
195
197 constexpr auto operator<(const strong_type& rhs) const& noexcept(noexcept(m_value < rhs.m_value)) -> bool
198 requires std::totally_ordered<value_type>
199 {
200 return m_value < rhs.m_value;
201 }
202
204 constexpr auto operator>(const strong_type& rhs) const& noexcept(noexcept(m_value > rhs.m_value)) -> bool
205 requires std::totally_ordered<value_type>
206 {
207 return m_value > rhs.m_value;
208 }
209
211 constexpr auto operator<=(const strong_type& rhs) const& noexcept(noexcept(m_value <= rhs.m_value)) -> bool
212 requires std::totally_ordered<value_type>
213 {
214 return m_value <= rhs.m_value;
215 }
216
218 constexpr auto operator>=(const strong_type& rhs) const& noexcept(noexcept(m_value >= rhs.m_value)) -> bool
219 requires std::totally_ordered<value_type>
220 {
221 return m_value >= rhs.m_value;
222 }
223
225 constexpr auto operator<=>(const strong_type& rhs) const& noexcept(noexcept(m_value <=>
226 rhs.m_value)) -> std::strong_ordering
227 requires std::three_way_comparable<value_type>
228 {
229 return m_value <=> rhs.m_value;
230 }
231
232 //
233 // Conversion operators
234 //
235
237 constexpr explicit operator const value_type&() const& noexcept { return m_value; }
238
240 constexpr explicit operator value_type&() & noexcept { return m_value; }
241
243 constexpr explicit operator const value_type&&() const&& noexcept { return std::move(m_value); }
244
246 constexpr explicit operator value_type&&() && noexcept { return std::move(m_value); }
247
248 //
249 // Increment and decrement operators
250 //
251
253 constexpr auto operator++() & noexcept(noexcept(++m_value)) -> strong_type&
255 {
256 ++m_value;
257 return *this;
258 }
259
261 constexpr auto operator++() && noexcept(noexcept(++m_value)) -> strong_type&&
263 {
264 ++m_value;
265 return std::move(*this);
266 }
267
269 constexpr auto operator++(int) & noexcept(noexcept(m_value++)) -> strong_type
271 {
272 return strong_type{m_value++};
273 }
274
276 constexpr auto operator++(int) && noexcept(noexcept(m_value++)) -> strong_type
278 {
279 return strong_type{m_value++};
280 }
281
283 constexpr auto operator--() & noexcept(noexcept(--m_value)) -> strong_type&
285 {
286 --m_value;
287 return *this;
288 }
289
291 constexpr auto operator--() && noexcept(noexcept(--m_value)) -> strong_type&&
293 {
294 --m_value;
295 return std::move(*this);
296 }
297
299 constexpr auto operator--(int) & noexcept(noexcept(m_value--)) -> strong_type
301 {
302 return strong_type{m_value--};
303 }
304
306 constexpr auto operator--(int) && noexcept(noexcept(m_value--)) -> strong_type
308 {
309 return strong_type{m_value--};
310 }
311
312 //
313 // Arithmetic operators
314 //
315
317 constexpr auto operator+() const& noexcept(noexcept(+m_value)) -> strong_type
318 requires std::signed_integral<value_type>
319 {
320 return strong_type{+m_value};
321 }
322
324 constexpr auto operator+() && noexcept(noexcept(+m_value)) -> strong_type
325 requires std::signed_integral<value_type>
326 {
327 return strong_type{+m_value};
328 }
329
331 constexpr auto operator-() const& noexcept(noexcept(-m_value)) -> strong_type
332 requires std::signed_integral<value_type>
333 {
334 return strong_type{-m_value};
335 }
336
338 constexpr auto operator-() && noexcept(noexcept(-m_value)) -> strong_type
339 requires std::signed_integral<value_type>
340 {
341 return strong_type{-m_value};
342 }
343
345 constexpr auto operator+(const strong_type& rhs) const& noexcept(noexcept(m_value + rhs.m_value)) -> strong_type
347 {
348 return strong_type{m_value + rhs.m_value};
349 }
350
352 constexpr auto operator+(strong_type&& rhs) const& noexcept(noexcept(m_value + rhs.m_value)) -> strong_type
354 {
355 return strong_type{m_value + rhs.m_value};
356 }
357
359 constexpr auto operator+(const strong_type& rhs) && noexcept(noexcept(m_value + rhs.m_value)) -> strong_type
361 {
362 return strong_type{m_value + rhs.m_value};
363 }
364
366 constexpr auto operator+(strong_type&& rhs) && noexcept(noexcept(m_value + rhs.m_value)) -> strong_type
368 {
369 return strong_type{m_value + rhs.m_value};
370 }
371
373 constexpr auto operator-(const strong_type& rhs) const& noexcept(noexcept(m_value - rhs.m_value)) -> strong_type
375 {
376 return strong_type{m_value - rhs.m_value};
377 }
378
380 constexpr auto operator-(strong_type&& rhs) const& noexcept(noexcept(m_value - rhs.m_value)) -> strong_type
382 {
383 return strong_type{m_value - rhs.m_value};
384 }
385
387 constexpr auto operator-(const strong_type& rhs) && noexcept(noexcept(m_value - rhs.m_value)) -> strong_type
389 {
390 return strong_type{m_value - rhs.m_value};
391 }
392
394 constexpr auto operator-(strong_type&& rhs) && noexcept(noexcept(m_value - rhs.m_value)) -> strong_type
396 {
397 return strong_type{m_value - rhs.m_value};
398 }
399
401 constexpr auto operator*(const strong_type& rhs) const& noexcept(noexcept(m_value * rhs.m_value)) -> strong_type
403 {
404 return strong_type{m_value * rhs.m_value};
405 }
406
408 constexpr auto operator*(strong_type&& rhs) const& noexcept(noexcept(m_value * rhs.m_value)) -> strong_type
410 {
411 return strong_type{m_value * rhs.m_value};
412 }
413
415 constexpr auto operator*(const strong_type& rhs) && noexcept(noexcept(m_value * rhs.m_value)) -> strong_type
417 {
418 return strong_type{m_value * rhs.m_value};
419 }
420
422 constexpr auto operator*(strong_type&& rhs) && noexcept(noexcept(m_value * rhs.m_value)) -> strong_type
424 {
425 return strong_type{m_value * rhs.m_value};
426 }
427
429 constexpr auto operator/(const strong_type& rhs) const& noexcept(noexcept(m_value / rhs.m_value)) -> strong_type
431 {
432 return strong_type{m_value / rhs.m_value};
433 }
434
436 constexpr auto operator/(strong_type&& rhs) const& noexcept(noexcept(m_value / rhs.m_value)) -> strong_type
438 {
439 return strong_type{m_value / rhs.m_value};
440 }
441
443 constexpr auto operator/(const strong_type& rhs) && noexcept(noexcept(m_value / rhs.m_value)) -> strong_type
445 {
446 return strong_type{m_value / rhs.m_value};
447 }
448
450 constexpr auto operator/(strong_type&& rhs) && noexcept(noexcept(m_value / rhs.m_value)) -> strong_type
452 {
453 return strong_type{m_value / rhs.m_value};
454 }
455
457 constexpr auto operator%(const strong_type& rhs) const& noexcept(noexcept(m_value % rhs.m_value)) -> strong_type
459 {
460 return strong_type{m_value % rhs.m_value};
461 }
462
464 constexpr auto operator%(strong_type&& rhs) const& noexcept(noexcept(m_value % rhs.m_value)) -> strong_type
466 {
467 return strong_type{m_value % rhs.m_value};
468 }
469
471 constexpr auto operator%(const strong_type& rhs) && noexcept(noexcept(m_value % rhs.m_value)) -> strong_type
473 {
474 return strong_type{m_value % rhs.m_value};
475 }
476
478 constexpr auto operator%(strong_type&& rhs) && noexcept(noexcept(m_value % rhs.m_value)) -> strong_type
480 {
481 return strong_type{m_value % rhs.m_value};
482 }
483
485 constexpr auto operator+=(const strong_type& rhs) & noexcept(noexcept(m_value += rhs.m_value)) -> strong_type&
487 {
488 m_value += rhs.m_value;
489 return *this;
490 }
491
493 constexpr auto operator+=(strong_type&& rhs) & noexcept(noexcept(m_value += rhs.m_value)) -> strong_type&
494 requires arithmetic<value_type>
495 {
496 m_value += rhs.m_value;
497 return *this;
498 }
499
501 constexpr auto operator-=(const strong_type& rhs) & noexcept(noexcept(m_value -= rhs.m_value)) -> strong_type&
502 requires arithmetic<value_type>
503 {
504 m_value -= rhs.m_value;
505 return *this;
506 }
507
509 constexpr auto operator-=(strong_type&& rhs) & noexcept(noexcept(m_value -= rhs.m_value)) -> strong_type&
510 requires arithmetic<value_type>
511 {
512 m_value -= rhs.m_value;
513 return *this;
514 }
515
517 constexpr auto operator*=(const strong_type& rhs) & noexcept(noexcept(m_value *= rhs.m_value)) -> strong_type&
518 requires arithmetic<value_type>
519 {
520 m_value *= rhs.m_value;
521 return *this;
522 }
523
525 constexpr auto operator*=(strong_type&& rhs) & noexcept(noexcept(m_value *= rhs.m_value)) -> strong_type&
526 requires arithmetic<value_type>
527 {
528 m_value *= rhs.m_value;
529 return *this;
530 }
531
533 constexpr auto operator/=(const strong_type& rhs) & noexcept(noexcept(m_value /= rhs.m_value)) -> strong_type&
534 requires arithmetic<value_type>
535 {
536 m_value /= rhs.m_value;
537 return *this;
538 }
539
541 constexpr auto operator/=(strong_type&& rhs) & noexcept(noexcept(m_value /= rhs.m_value)) -> strong_type&
542 requires arithmetic<value_type>
543 {
544 m_value /= rhs.m_value;
545 return *this;
546 }
547
549 constexpr auto operator%=(const strong_type& rhs) & noexcept(noexcept(m_value %= rhs.m_value)) -> strong_type&
550 requires arithmetic<value_type>
551 {
552 m_value %= rhs.m_value;
553 return *this;
554 }
555
557 constexpr auto operator%=(strong_type&& rhs) & noexcept(noexcept(m_value %= rhs.m_value)) -> strong_type&
558 requires arithmetic<value_type>
559 {
560 m_value %= rhs.m_value;
561 return *this;
562 }
563
564 //
565 // Bitwise operators
566 //
567
569 constexpr auto operator~() const& noexcept(noexcept(~m_value)) -> strong_type
570 requires std::unsigned_integral<value_type>
571 {
572 return strong_type{~m_value};
573 }
574
576 constexpr auto operator&(const strong_type& rhs) const& noexcept(noexcept(m_value & rhs.m_value)) -> strong_type
577 requires std::unsigned_integral<value_type>
578 {
579 return strong_type{m_value & rhs.m_value};
580 }
581
583 constexpr auto operator&(strong_type&& rhs) const& noexcept(noexcept(m_value & rhs.m_value)) -> strong_type
584 requires std::unsigned_integral<value_type>
585 {
586 return strong_type{m_value & rhs.m_value};
587 }
588
590 constexpr auto operator&(const strong_type& rhs) && noexcept(noexcept(m_value & rhs.m_value)) -> strong_type
591 requires std::unsigned_integral<value_type>
592 {
593 return strong_type{m_value & rhs.m_value};
594 }
595
597 constexpr auto operator&(strong_type&& rhs) && noexcept(noexcept(m_value & rhs.m_value)) -> strong_type
598 requires std::unsigned_integral<value_type>
599 {
600 return strong_type{m_value & rhs.m_value};
601 }
602
604 constexpr auto operator|(const strong_type& rhs) const& noexcept(noexcept(m_value | rhs.m_value)) -> strong_type
605 requires std::unsigned_integral<value_type>
606 {
607 return strong_type{m_value | rhs.m_value};
608 }
609
611 constexpr auto operator|(strong_type&& rhs) const& noexcept(noexcept(m_value | rhs.m_value)) -> strong_type
612 requires std::unsigned_integral<value_type>
613 {
614 return strong_type{m_value | rhs.m_value};
615 }
616
618 constexpr auto operator|(const strong_type& rhs) && noexcept(noexcept(m_value | rhs.m_value)) -> strong_type
619 requires std::unsigned_integral<value_type>
620 {
621 return strong_type{m_value | rhs.m_value};
622 }
623
625 constexpr auto operator|(strong_type&& rhs) && noexcept(noexcept(m_value | rhs.m_value)) -> strong_type
626 requires std::unsigned_integral<value_type>
627 {
628 return strong_type{m_value | rhs.m_value};
629 }
630
632 constexpr auto operator^(const strong_type& rhs) const& noexcept(noexcept(m_value ^ rhs.m_value)) -> strong_type
633 requires std::unsigned_integral<value_type>
634 {
635 return strong_type{m_value ^ rhs.m_value};
636 }
637
639 constexpr auto operator^(strong_type&& rhs) const& noexcept(noexcept(m_value ^ rhs.m_value)) -> strong_type
640 requires std::unsigned_integral<value_type>
641 {
642 return strong_type{m_value ^ rhs.m_value};
643 }
644
646 constexpr auto operator^(const strong_type& rhs) && noexcept(noexcept(m_value ^ rhs.m_value)) -> strong_type
647 requires std::unsigned_integral<value_type>
648 {
649 return strong_type{m_value ^ rhs.m_value};
650 }
651
653 constexpr auto operator^(strong_type&& rhs) && noexcept(noexcept(m_value ^ rhs.m_value)) -> strong_type
654 requires std::unsigned_integral<value_type>
655 {
656 return strong_type{m_value ^ rhs.m_value};
657 }
658
660 constexpr auto operator<<(const strong_type& rhs) const& noexcept(noexcept(m_value << rhs.m_value)) -> strong_type
661 requires std::unsigned_integral<value_type>
662 {
663 return strong_type{m_value << rhs.m_value};
664 }
665
667 constexpr auto operator<<(strong_type&& rhs) const& noexcept(noexcept(m_value << rhs.m_value)) -> strong_type
668 requires std::unsigned_integral<value_type>
669 {
670 return strong_type{m_value << rhs.m_value};
671 }
672
674 constexpr auto operator<<(const strong_type& rhs) && noexcept(noexcept(m_value << rhs.m_value)) -> strong_type
675 requires std::unsigned_integral<value_type>
676 {
677 return strong_type{m_value << rhs.m_value};
678 }
679
681 constexpr auto operator<<(strong_type&& rhs) && noexcept(noexcept(m_value << rhs.m_value)) -> strong_type
682 requires std::unsigned_integral<value_type>
683 {
684 return strong_type{m_value << rhs.m_value};
685 }
686
688 constexpr auto operator>>(const strong_type& rhs) const& noexcept(noexcept(m_value >> rhs.m_value)) -> strong_type
689 requires std::unsigned_integral<value_type>
690 {
691 return strong_type{m_value >> rhs.m_value};
692 }
693
695 constexpr auto operator>>(strong_type&& rhs) const& noexcept(noexcept(m_value >> rhs.m_value)) -> strong_type
696 requires std::unsigned_integral<value_type>
697 {
698 return strong_type{m_value >> rhs.m_value};
699 }
700
702 constexpr auto operator>>(const strong_type& rhs) && noexcept(noexcept(m_value >> rhs.m_value)) -> strong_type
703 requires std::unsigned_integral<value_type>
704 {
705 return strong_type{m_value >> rhs.m_value};
706 }
707
709 constexpr auto operator>>(strong_type&& rhs) && noexcept(noexcept(m_value >> rhs.m_value)) -> strong_type
710 requires std::unsigned_integral<value_type>
711 {
712 return strong_type{m_value >> rhs.m_value};
713 }
714
716 constexpr auto operator&=(const strong_type& rhs) & noexcept(noexcept(m_value &= rhs.m_value)) -> strong_type&
717 requires std::unsigned_integral<value_type>
718 {
719 m_value &= rhs.m_value;
720 return *this;
721 }
722
724 constexpr auto operator&=(strong_type&& rhs) & noexcept(noexcept(m_value &= rhs.m_value)) -> strong_type&
725 requires std::unsigned_integral<value_type>
726 {
727 m_value &= rhs.m_value;
728 return *this;
729 }
730
732 constexpr auto operator|=(const strong_type& rhs) & noexcept(noexcept(m_value |= rhs.m_value)) -> strong_type&
733 requires std::unsigned_integral<value_type>
734 {
735 m_value |= rhs.m_value;
736 return *this;
737 }
738
740 constexpr auto operator|=(strong_type&& rhs) & noexcept(noexcept(m_value |= rhs.m_value)) -> strong_type&
741 requires std::unsigned_integral<value_type>
742 {
743 m_value |= rhs.m_value;
744 return *this;
745 }
746
748 constexpr auto operator^=(const strong_type& rhs) & noexcept(noexcept(m_value ^= rhs.m_value)) -> strong_type&
749 requires std::unsigned_integral<value_type>
750 {
751 m_value ^= rhs.m_value;
752 return *this;
753 }
754
756 constexpr auto operator^=(strong_type&& rhs) & noexcept(noexcept(m_value ^= rhs.m_value)) -> strong_type&
757 requires std::unsigned_integral<value_type>
758 {
759 m_value ^= rhs.m_value;
760 return *this;
761 }
762
764 constexpr auto operator<<=(const strong_type& rhs) & noexcept(noexcept(m_value <<= rhs.m_value)) -> strong_type&
765 requires std::unsigned_integral<value_type>
766 {
767 m_value <<= rhs.m_value;
768 return *this;
769 }
770
772 constexpr auto operator<<=(strong_type&& rhs) & noexcept(noexcept(m_value <<= rhs.m_value)) -> strong_type&
773 requires std::unsigned_integral<value_type>
774 {
775 m_value <<= rhs.m_value;
776 return *this;
777 }
778
780 constexpr auto operator>>=(const strong_type& rhs) & noexcept(noexcept(m_value >>= rhs.m_value)) -> strong_type&
781 requires std::unsigned_integral<value_type>
782 {
783 m_value >>= rhs.m_value;
784 return *this;
785 }
786
788 constexpr auto operator>>=(strong_type&& rhs) & noexcept(noexcept(m_value >>= rhs.m_value)) -> strong_type&
789 requires std::unsigned_integral<value_type>
790 {
791 m_value >>= rhs.m_value;
792 return *this;
793 }
794
795 //
796 // Ranges interface
797 //
798
800 constexpr auto begin() const noexcept(noexcept(std::ranges::begin(m_value)))
801 requires std::ranges::range<value_type>
802 {
803 return std::ranges::begin(m_value);
804 }
805
807 constexpr auto begin() noexcept(noexcept(std::ranges::begin(m_value)))
808 requires std::ranges::range<value_type>
809 {
810 return std::ranges::begin(m_value);
811 }
812
814 constexpr auto end() const noexcept(noexcept(std::ranges::end(m_value)))
815 requires std::ranges::range<value_type>
816 {
817 return std::ranges::end(m_value);
818 }
819
821 constexpr auto end() noexcept(noexcept(std::ranges::end(m_value)))
822 requires std::ranges::range<value_type>
823 {
824 return std::ranges::end(m_value);
825 }
826
827 //
828 // Stream operators
829 //
830
832 friend inline auto operator<<(std::ostream& ostream,
833 const strong_type& rhs) noexcept(noexcept(ostream << rhs.m_value)) -> std::ostream&
835 {
836 return ostream << rhs.m_value;
837 }
838
840 friend inline auto operator>>(std::istream& istream,
841 strong_type& rhs) noexcept(noexcept(istream >> rhs.m_value)) -> std::istream&
843 {
844 return istream >> rhs.m_value;
845 }
846
847 private:
848 value_type m_value{};
849};
850
851//
852// Creation functions
853//
854
856template <typename Tag, typename T, typename... Args>
857constexpr auto make_strong_type(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
858 requires std::constructible_from<std::remove_cvref_t<T>, Args...>
859{
860 return strong_type<std::remove_cvref_t<T>, Tag>{std::forward<Args>(args)...};
861}
862
864template <typename Tag, typename T, typename U, typename... Args>
865constexpr auto make_strong_type(std::initializer_list<U> ilist, Args&&... args) noexcept(
866 std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>)
867 requires std::constructible_from<T, std::initializer_list<U>&, Args...>
868{
869 return strong_type<std::remove_cvref_t<T>, Tag>{ilist, std::forward<Args>(args)...};
870}
871
873template <typename Tag, typename T>
874constexpr auto make_strong_type(T&& value) noexcept(
875 std::is_nothrow_constructible_v<strong_type<std::remove_cvref_t<T>, Tag>, T>)
876 requires std::constructible_from<std::remove_cvref_t<T>, T>
877{
878 return strong_type<std::remove_cvref_t<T>, Tag>{std::forward<T>(value)};
879}
880
881} // namespace gw
882
883namespace std {
884
885//
886// Hash calculation
887//
888
890template <::gw::hashable T, ::gw::complete Tag>
891// NOLINTNEXTLINE(cert-dcl58-cpp)
892struct hash<::gw::strong_type<T, Tag>> {
893 [[nodiscard]] auto inline operator()(const ::gw::strong_type<T, Tag>& strong_type) const noexcept -> size_t {
894 auto tag_hash = hash<type_index>{}(type_index{typeid(Tag)});
895 auto value_hash = hash<T>{}(strong_type.value());
896 return tag_hash ^ value_hash;
897 }
898};
899
900//
901// String conversion
902//
903
906template <typename T, typename Tag, class CharT>
907// NOLINTNEXTLINE(cert-dcl58-cpp)
908struct formatter<::gw::strong_type<T, Tag>, CharT> {
911 template <class ParseContext>
912 constexpr auto parse(ParseContext& context) -> ParseContext::iterator {
913 return context.begin();
914 }
915
918 template <class FormatContext>
919 auto format(const ::gw::strong_type<T, Tag>& strong_type, FormatContext& context) const -> FormatContext::iterator
920#if __cplusplus > 202002L
921 requires std::formattable<T, CharT>
922#endif // __cplusplus > 202002L
923 {
924 if constexpr (::gw::named<Tag>) {
925 return format_to(context.out(), "{}: {}", Tag::name(), strong_type.value());
926 }
927
928 return format_to(context.out(), "{}", strong_type.value());
929 }
930};
931
932} // namespace std
Strong type wrapper.
Definition strong_type.hpp:46
constexpr auto transform(F &&func) &noexcept(noexcept(func(m_value))) -> strong_type
returns a gw::strong_type containing the transformed contained value
Definition strong_type.hpp:127
constexpr auto operator%(const strong_type &rhs) &&noexcept(noexcept(m_value % rhs.m_value)) -> strong_type
calculates the remainder of the contained values
Definition strong_type.hpp:471
constexpr auto operator%(const strong_type &rhs) const &noexcept(noexcept(m_value % rhs.m_value)) -> strong_type
calculates the remainder of the contained values
Definition strong_type.hpp:457
constexpr auto operator+(const strong_type &rhs) const &noexcept(noexcept(m_value+rhs.m_value)) -> strong_type
adds the contained values
Definition strong_type.hpp:345
constexpr auto begin() const noexcept(noexcept(std::ranges::begin(m_value)))
returns an iterator to the beginning of the contained value
Definition strong_type.hpp:800
constexpr auto operator+(strong_type &&rhs) &&noexcept(noexcept(m_value+rhs.m_value)) -> strong_type
adds the contained values
Definition strong_type.hpp:366
constexpr auto operator/(strong_type &&rhs) const &noexcept(noexcept(m_value/rhs.m_value)) -> strong_type
devides the contained values
Definition strong_type.hpp:436
constexpr auto operator-() const &noexcept(noexcept(-m_value)) -> strong_type
negates the contained value
Definition strong_type.hpp:331
constexpr auto operator<<(const strong_type &rhs) const &noexcept(noexcept(m_value<< rhs.m_value)) -> strong_type
performs binary left shift on the contained values
Definition strong_type.hpp:660
constexpr auto operator>>(strong_type &&rhs) const &noexcept(noexcept(m_value > > rhs.m_value)) -> strong_type
performs binary right shift on the contained values
Definition strong_type.hpp:695
constexpr auto operator/(strong_type &&rhs) &&noexcept(noexcept(m_value/rhs.m_value)) -> strong_type
devides the contained values
Definition strong_type.hpp:450
constexpr strong_type(std::initializer_list< U > ilist, Args &&... args) noexcept(std::is_nothrow_constructible_v< value_type, std::initializer_list< U > &, Args... >)
constructs the gw::strong_type object
Definition strong_type.hpp:67
constexpr auto operator-(strong_type &&rhs) &&noexcept(noexcept(m_value - rhs.m_value)) -> strong_type
subtracts the contained values
Definition strong_type.hpp:394
constexpr auto operator^(strong_type &&rhs) &&noexcept(noexcept(m_value ^ rhs.m_value)) -> strong_type
performs binary XOR on the contained values
Definition strong_type.hpp:653
constexpr auto operator>>(strong_type &&rhs) &&noexcept(noexcept(m_value > > rhs.m_value)) -> strong_type
performs binary right shift on the contained values
Definition strong_type.hpp:709
constexpr auto operator<(const strong_type &rhs) const &noexcept(noexcept(m_value< rhs.m_value)) -> bool
compares gw::strong_type objects
Definition strong_type.hpp:197
constexpr auto value() &noexcept -> value_type &
returns the contained value
Definition strong_type.hpp:105
constexpr auto operator>>(const strong_type &rhs) const &noexcept(noexcept(m_value > > rhs.m_value)) -> strong_type
performs binary right shift on the contained values
Definition strong_type.hpp:688
constexpr auto end() const noexcept(noexcept(std::ranges::end(m_value)))
returns an iterator to the end of the contained value
Definition strong_type.hpp:814
constexpr auto operator-(const strong_type &rhs) &&noexcept(noexcept(m_value - rhs.m_value)) -> strong_type
subtracts the contained values
Definition strong_type.hpp:387
constexpr auto value() const &&noexcept -> const value_type &&
returns the contained value
Definition strong_type.hpp:108
constexpr auto operator^(strong_type &&rhs) const &noexcept(noexcept(m_value ^ rhs.m_value)) -> strong_type
performs binary XOR on the contained values
Definition strong_type.hpp:639
constexpr auto operator>>(const strong_type &rhs) &&noexcept(noexcept(m_value > > rhs.m_value)) -> strong_type
performs binary right shift on the contained values
Definition strong_type.hpp:702
constexpr auto operator->() noexcept -> value_type *
accesses the contained value
Definition strong_type.hpp:87
constexpr auto operator&(strong_type &&rhs) const &noexcept(noexcept(m_value &rhs.m_value)) -> strong_type
performs binary AND on the contained values
Definition strong_type.hpp:583
constexpr auto operator==(const strong_type &rhs) const &noexcept(noexcept(m_value==rhs.m_value)) -> bool
compares gw::strong_type objects
Definition strong_type.hpp:183
constexpr auto operator-() &&noexcept(noexcept(-m_value)) -> strong_type
negates the contained value
Definition strong_type.hpp:338
constexpr auto operator%(strong_type &&rhs) &&noexcept(noexcept(m_value % rhs.m_value)) -> strong_type
calculates the remainder of the contained values
Definition strong_type.hpp:478
constexpr auto operator>(const strong_type &rhs) const &noexcept(noexcept(m_value > rhs.m_value)) -> bool
compares gw::strong_type objects
Definition strong_type.hpp:204
constexpr auto operator-(const strong_type &rhs) const &noexcept(noexcept(m_value - rhs.m_value)) -> strong_type
subtracts the contained values
Definition strong_type.hpp:373
constexpr auto transform(F &&func) &&noexcept(noexcept(func(m_value))) -> strong_type
returns a gw::strong_type containing the transformed contained value
Definition strong_type.hpp:144
constexpr auto operator*(strong_type &&rhs) const &noexcept(noexcept(m_value *rhs.m_value)) -> strong_type
multiplies the contained values
Definition strong_type.hpp:408
constexpr auto operator&(const strong_type &rhs) const &noexcept(noexcept(m_value &rhs.m_value)) -> strong_type
performs binary AND on the contained values
Definition strong_type.hpp:576
constexpr auto operator--() &noexcept(noexcept(--m_value)) -> strong_type &
decrements the contained value
Definition strong_type.hpp:283
constexpr auto value() const &noexcept -> const value_type &
returns the contained value
Definition strong_type.hpp:102
constexpr auto operator++(int) &noexcept(noexcept(m_value++)) -> strong_type
increments the contained value
Definition strong_type.hpp:269
constexpr auto operator/(const strong_type &rhs) &&noexcept(noexcept(m_value/rhs.m_value)) -> strong_type
devides the contained values
Definition strong_type.hpp:443
constexpr auto operator^(const strong_type &rhs) &&noexcept(noexcept(m_value ^ rhs.m_value)) -> strong_type
performs binary XOR on the contained values
Definition strong_type.hpp:646
constexpr auto operator|(strong_type &&rhs) const &noexcept(noexcept(m_value|rhs.m_value)) -> strong_type
performs binary OR on the contained values
Definition strong_type.hpp:611
constexpr auto operator+() const &noexcept(noexcept(+m_value)) -> strong_type
affirms the contained value
Definition strong_type.hpp:317
constexpr auto operator<<(strong_type &&rhs) &&noexcept(noexcept(m_value<< rhs.m_value)) -> strong_type
performs binary left shift on the contained values
Definition strong_type.hpp:681
~strong_type() noexcept(std::is_nothrow_destructible_v< value_type >)=default
destroys the contained value
constexpr auto value() &&noexcept -> value_type &&
returns the contained value
Definition strong_type.hpp:111
constexpr auto operator<<(const strong_type &rhs) &&noexcept(noexcept(m_value<< rhs.m_value)) -> strong_type
performs binary left shift on the contained values
Definition strong_type.hpp:674
constexpr auto operator+(strong_type &&rhs) const &noexcept(noexcept(m_value+rhs.m_value)) -> strong_type
adds the contained values
Definition strong_type.hpp:352
constexpr auto operator|(const strong_type &rhs) const &noexcept(noexcept(m_value|rhs.m_value)) -> strong_type
performs binary OR on the contained values
Definition strong_type.hpp:604
constexpr auto operator*() &&noexcept -> value_type &&
accesses the contained value
Definition strong_type.hpp:99
constexpr auto operator*(const strong_type &rhs) &&noexcept(noexcept(m_value *rhs.m_value)) -> strong_type
multiplies the contained values
Definition strong_type.hpp:415
constexpr auto operator<<(strong_type &&rhs) const &noexcept(noexcept(m_value<< rhs.m_value)) -> strong_type
performs binary left shift on the contained values
Definition strong_type.hpp:667
constexpr auto operator++() &noexcept(noexcept(++m_value)) -> strong_type &
increments the contained value
Definition strong_type.hpp:253
constexpr auto operator&(strong_type &&rhs) &&noexcept(noexcept(m_value &rhs.m_value)) -> strong_type
performs binary AND on the contained values
Definition strong_type.hpp:597
constexpr auto operator|(const strong_type &rhs) &&noexcept(noexcept(m_value|rhs.m_value)) -> strong_type
performs binary OR on the contained values
Definition strong_type.hpp:618
constexpr auto operator*() const &noexcept -> const value_type &
accesses the contained value
Definition strong_type.hpp:90
Tag tag_type
The tag type.
Definition strong_type.hpp:53
T value_type
The type of the contained value.
Definition strong_type.hpp:52
constexpr strong_type(Args &&... args) noexcept(std::is_nothrow_constructible_v< value_type, Args... >)
Construct the gw::strong_type object.
Definition strong_type.hpp:61
constexpr auto operator/(const strong_type &rhs) const &noexcept(noexcept(m_value/rhs.m_value)) -> strong_type
devides the contained values
Definition strong_type.hpp:429
constexpr auto operator*(strong_type &&rhs) &&noexcept(noexcept(m_value *rhs.m_value)) -> strong_type
multiplies the contained values
Definition strong_type.hpp:422
constexpr auto operator|(strong_type &&rhs) &&noexcept(noexcept(m_value|rhs.m_value)) -> strong_type
performs binary OR on the contained values
Definition strong_type.hpp:625
constexpr auto operator&(const strong_type &rhs) &&noexcept(noexcept(m_value &rhs.m_value)) -> strong_type
performs binary AND on the contained values
Definition strong_type.hpp:590
constexpr auto begin() noexcept(noexcept(std::ranges::begin(m_value)))
returns an iterator to the beginning of the contained value
Definition strong_type.hpp:807
constexpr auto operator*() &noexcept -> value_type &
accesses the contained value
Definition strong_type.hpp:93
constexpr auto operator*(const strong_type &rhs) const &noexcept(noexcept(m_value *rhs.m_value)) -> strong_type
multiplies the contained values
Definition strong_type.hpp:401
friend auto operator<<(std::ostream &ostream, const strong_type &rhs) noexcept(noexcept(ostream<< rhs.m_value)) -> std::ostream &
inserts formatted data
Definition strong_type.hpp:832
friend auto operator>>(std::istream &istream, strong_type &rhs) noexcept(noexcept(istream > > rhs.m_value)) -> std::istream &
extracts formatted data
Definition strong_type.hpp:840
constexpr auto operator++(int) &&noexcept(noexcept(m_value++)) -> strong_type
increments the contained value
Definition strong_type.hpp:276
constexpr auto operator--() &&noexcept(noexcept(--m_value)) -> strong_type &&
decrements the contained value
Definition strong_type.hpp:291
constexpr auto emplace(Args &&... args) noexcept(std::is_nothrow_constructible_v< value_type, Args... >) -> value_type &
constructs the contained value in-place
Definition strong_type.hpp:171
constexpr auto operator--(int) &&noexcept(noexcept(m_value--)) -> strong_type
decrements the contained value
Definition strong_type.hpp:306
constexpr void reset() noexcept(std::is_nothrow_default_constructible_v< value_type >)
destroys any contained value
Definition strong_type.hpp:163
constexpr auto operator*() const &&noexcept -> const value_type &&
accesses the contained value
Definition strong_type.hpp:96
constexpr auto end() noexcept(noexcept(std::ranges::end(m_value)))
returns an iterator to the end of the contained value
Definition strong_type.hpp:821
constexpr auto transform(F &&func) const &noexcept(noexcept(func(m_value))) -> strong_type
returns a gw::strong_type containing the transformed contained value
Definition strong_type.hpp:119
constexpr void swap(strong_type &rhs) noexcept(std::is_nothrow_swappable_v< value_type >)
specializes the std::swap algorithm
Definition strong_type.hpp:155
constexpr auto operator+(const strong_type &rhs) &&noexcept(noexcept(m_value+rhs.m_value)) -> strong_type
adds the contained values
Definition strong_type.hpp:359
constexpr auto operator++() &&noexcept(noexcept(++m_value)) -> strong_type &&
increments the contained value
Definition strong_type.hpp:261
constexpr auto operator--(int) &noexcept(noexcept(m_value--)) -> strong_type
decrements the contained value
Definition strong_type.hpp:299
constexpr auto operator+() &&noexcept(noexcept(+m_value)) -> strong_type
affirms the contained value
Definition strong_type.hpp:324
constexpr auto operator~() const &noexcept(noexcept(~m_value)) -> strong_type
inverts the contained value
Definition strong_type.hpp:569
constexpr auto transform(F &&func) const &&noexcept(noexcept(func(m_value))) -> strong_type
returns a gw::strong_type containing the transformed contained value
Definition strong_type.hpp:135
constexpr auto operator^(const strong_type &rhs) const &noexcept(noexcept(m_value ^ rhs.m_value)) -> strong_type
performs binary XOR on the contained values
Definition strong_type.hpp:632
constexpr auto operator%(strong_type &&rhs) const &noexcept(noexcept(m_value % rhs.m_value)) -> strong_type
calculates the remainder of the contained values
Definition strong_type.hpp:464
constexpr auto operator-(strong_type &&rhs) const &noexcept(noexcept(m_value - rhs.m_value)) -> strong_type
subtracts the contained values
Definition strong_type.hpp:380
Concept for arithmetic types.
Definition concepts.hpp:18
Concept for decrementable types.
Definition concepts.hpp:29
Concept for incrementable types.
Definition concepts.hpp:22
Concept for istreamable types.
Definition concepts.hpp:52
Concept for ostreamable types.
Definition concepts.hpp:46
GW namespace.
Definition concepts.hpp:14
constexpr auto make_strong_type(Args &&... args) noexcept(std::is_nothrow_constructible_v< T, Args... >)
creates a gw::strong_type object
Definition strong_type.hpp:857
STL namespace.