10 #include <initializer_list>
14 #include <type_traits>
18 #include "gw/concepts.hpp"
25 struct strong_type_empty_base {
26 constexpr
auto operator<=>(
const strong_type_empty_base&)
const noexcept =
default;
42 template <
typename T,
typename Tag>
44 :
public std::conditional_t<std::ranges::range<T>, std::ranges::view_interface<strong_type<T, Tag>>,
45 detail::strong_type_empty_base> {
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)...}) {}
68 template <
typename U,
typename... Args>
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)...}) {}
79 ~strong_type() noexcept(std::is_nothrow_destructible_v<T>) = default;
86 constexpr auto operator->() const noexcept -> const T* {
return &m_value; }
89 constexpr
auto operator->() noexcept -> T* {
return &m_value; }
92 constexpr
auto operator*() const& noexcept -> const T& {
return m_value; }
95 constexpr
auto operator*() & noexcept -> T& {
return m_value; }
98 constexpr
auto operator*() const&& noexcept -> const T&& {
return std::move(m_value); }
101 constexpr
auto operator*() && noexcept -> T&& {
return std::move(m_value); }
104 constexpr
auto value() const& noexcept -> const T& {
return m_value; }
107 constexpr
auto value() & noexcept -> T& {
return m_value; }
110 constexpr
auto value() const&& noexcept -> const T&& {
return std::move(m_value); }
113 constexpr
auto value() && noexcept -> T&& {
return std::move(m_value); }
120 template <
typename F>
121 constexpr
auto transform(F&& func)
const& noexcept(noexcept(func(m_value)))
122 requires std::invocable<F, const T&>
128 template <
typename F>
129 constexpr
auto transform(F&& func) & noexcept(noexcept(func(m_value)))
130 requires std::invocable<F, T&>
132 return strong_type<std::remove_cv_t<std::invoke_result_t<F, T&>>, Tag>{func(m_value)};
136 template <
typename F>
137 constexpr
auto transform(F&& func)
const&& noexcept(noexcept(func(m_value)))
138 requires std::invocable<F, const T&&>
140 return strong_type<std::remove_cv_t<std::invoke_result_t<F, const T&&>>, Tag>{func(std::move(m_value))};
144 template <
typename F>
145 constexpr
auto transform(F&& func) && noexcept(noexcept(func(m_value)))
146 requires std::invocable<F, T&&>
148 return strong_type<std::remove_cv_t<std::invoke_result_t<F, T&&>>, Tag>{func(std::move(m_value))};
156 constexpr
void swap(
strong_type& rhs) noexcept(std::is_nothrow_swappable_v<T>)
157 requires std::swappable<T>
160 swap(m_value, rhs.m_value);
164 constexpr
void reset() noexcept(std::is_nothrow_default_constructible_v<T>)
165 requires std::default_initializable<T>
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...>
175 m_value = T{std::forward<Args>(args)...};
184 constexpr
auto operator==(
const strong_type& rhs)
const& noexcept(noexcept(m_value == rhs.m_value)) ->
bool
185 requires std::equality_comparable<T>
187 return m_value == rhs.m_value;
191 constexpr
auto operator!=(
const strong_type& rhs)
const& noexcept(noexcept(m_value != rhs.m_value)) ->
bool
192 requires std::equality_comparable<T>
194 return m_value != rhs.m_value;
198 constexpr
auto operator<(
const strong_type& rhs)
const& noexcept(noexcept(m_value < rhs.m_value)) ->
bool
199 requires std::totally_ordered<T>
201 return m_value < rhs.m_value;
205 constexpr
auto operator>(
const strong_type& rhs)
const& noexcept(noexcept(m_value > rhs.m_value)) ->
bool
206 requires std::totally_ordered<T>
208 return m_value > rhs.m_value;
212 constexpr
auto operator<=(
const strong_type& rhs)
const& noexcept(noexcept(m_value <= rhs.m_value)) ->
bool
213 requires std::totally_ordered<T>
215 return m_value <= rhs.m_value;
219 constexpr
auto operator>=(
const strong_type& rhs)
const& noexcept(noexcept(m_value >= rhs.m_value)) ->
bool
220 requires std::totally_ordered<T>
222 return m_value >= rhs.m_value;
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>
230 return m_value <=> rhs.m_value;
238 constexpr
explicit operator const T&()
const& noexcept {
return m_value; }
241 constexpr
explicit operator T&() & noexcept {
return m_value; }
244 constexpr
explicit operator const T&&()
const&& noexcept {
return std::move(m_value); }
247 constexpr
explicit operator T&&() && noexcept {
return std::move(m_value); }
254 constexpr
auto operator++() & noexcept(noexcept(++m_value)) ->
strong_type&
255 requires incrementable<T>
262 constexpr
auto operator++() && noexcept(noexcept(++m_value)) ->
strong_type&&
263 requires incrementable<T>
266 return std::move(*
this);
270 constexpr
auto operator++(
int) & noexcept(noexcept(m_value++)) ->
strong_type
271 requires incrementable<T>
277 constexpr
auto operator++(
int) && noexcept(noexcept(m_value++)) ->
strong_type
278 requires incrementable<T>
284 constexpr
auto operator--() & noexcept(noexcept(--m_value)) ->
strong_type&
285 requires decrementable<T>
292 constexpr
auto operator--() && noexcept(noexcept(--m_value)) ->
strong_type&&
293 requires decrementable<T>
296 return std::move(*
this);
300 constexpr
auto operator--(
int) & noexcept(noexcept(m_value--)) ->
strong_type
301 requires decrementable<T>
307 constexpr
auto operator--(
int) && noexcept(noexcept(m_value--)) ->
strong_type
308 requires decrementable<T>
318 constexpr
auto operator+() const& noexcept(noexcept(+m_value)) ->
strong_type
319 requires std::signed_integral<T>
325 constexpr
auto operator+() && noexcept(noexcept(+m_value)) ->
strong_type
326 requires std::signed_integral<T>
332 constexpr
auto operator-() const& noexcept(noexcept(-m_value)) ->
strong_type
333 requires std::signed_integral<T>
339 constexpr
auto operator-() && noexcept(noexcept(-m_value)) ->
strong_type
340 requires std::signed_integral<T>
346 constexpr
auto operator+(
const strong_type& rhs)
const& noexcept(noexcept(m_value + rhs.m_value)) ->
strong_type
347 requires arithmetic<T>
353 constexpr
auto operator+(
strong_type&& rhs)
const& noexcept(noexcept(m_value + rhs.m_value)) ->
strong_type
354 requires arithmetic<T>
360 constexpr
auto operator+(
const strong_type& rhs) && noexcept(noexcept(m_value + rhs.m_value)) ->
strong_type
361 requires arithmetic<T>
368 requires arithmetic<T>
374 constexpr
auto operator-(
const strong_type& rhs)
const& noexcept(noexcept(m_value - rhs.m_value)) ->
strong_type
375 requires arithmetic<T>
381 constexpr
auto operator-(
strong_type&& rhs)
const& noexcept(noexcept(m_value - rhs.m_value)) ->
strong_type
382 requires arithmetic<T>
388 constexpr
auto operator-(
const strong_type& rhs) && noexcept(noexcept(m_value - rhs.m_value)) ->
strong_type
389 requires arithmetic<T>
396 requires arithmetic<T>
403 requires arithmetic<T>
410 requires arithmetic<T>
417 requires arithmetic<T>
424 requires arithmetic<T>
430 constexpr
auto operator/(
const strong_type& rhs)
const& noexcept(noexcept(m_value / rhs.m_value)) ->
strong_type
431 requires arithmetic<T>
437 constexpr
auto operator/(
strong_type&& rhs)
const& noexcept(noexcept(m_value / rhs.m_value)) ->
strong_type
438 requires arithmetic<T>
444 constexpr
auto operator/(
const strong_type& rhs) && noexcept(noexcept(m_value / rhs.m_value)) ->
strong_type
445 requires arithmetic<T>
452 requires arithmetic<T>
458 constexpr
auto operator%(
const strong_type& rhs)
const& noexcept(noexcept(m_value % rhs.m_value)) ->
strong_type
459 requires arithmetic<T>
465 constexpr
auto operator%(
strong_type&& rhs)
const& noexcept(noexcept(m_value % rhs.m_value)) ->
strong_type
466 requires arithmetic<T>
472 constexpr
auto operator%(
const strong_type& rhs) && noexcept(noexcept(m_value % rhs.m_value)) ->
strong_type
473 requires arithmetic<T>
480 requires arithmetic<T>
486 constexpr
auto operator+=(
const strong_type& rhs) & noexcept(noexcept(m_value += rhs.m_value)) ->
strong_type&
487 requires arithmetic<T>
489 m_value += rhs.m_value;
495 requires arithmetic<T>
497 m_value += rhs.m_value;
502 constexpr
auto operator-=(
const strong_type& rhs) & noexcept(noexcept(m_value -= rhs.m_value)) ->
strong_type&
503 requires arithmetic<T>
505 m_value -= rhs.m_value;
511 requires arithmetic<T>
513 m_value -= rhs.m_value;
518 constexpr
auto operator*=(
const strong_type& rhs) & noexcept(noexcept(m_value *= rhs.m_value)) ->
strong_type&
519 requires arithmetic<T>
521 m_value *= rhs.m_value;
527 requires arithmetic<T>
529 m_value *= rhs.m_value;
534 constexpr
auto operator/=(
const strong_type& rhs) & noexcept(noexcept(m_value /= rhs.m_value)) ->
strong_type&
535 requires arithmetic<T>
537 m_value /= rhs.m_value;
543 requires arithmetic<T>
545 m_value /= rhs.m_value;
550 constexpr
auto operator%=(
const strong_type& rhs) & noexcept(noexcept(m_value %= rhs.m_value)) ->
strong_type&
551 requires arithmetic<T>
553 m_value %= rhs.m_value;
559 requires arithmetic<T>
561 m_value %= rhs.m_value;
570 constexpr
auto operator~() const& noexcept(noexcept(~m_value)) ->
strong_type
571 requires std::unsigned_integral<T>
577 constexpr
auto operator&(
const strong_type& rhs)
const& noexcept(noexcept(m_value & rhs.m_value)) ->
strong_type
578 requires std::unsigned_integral<T>
584 constexpr
auto operator&(
strong_type&& rhs)
const& noexcept(noexcept(m_value & rhs.m_value)) ->
strong_type
585 requires std::unsigned_integral<T>
591 constexpr
auto operator&(
const strong_type& rhs) && noexcept(noexcept(m_value & rhs.m_value)) ->
strong_type
592 requires std::unsigned_integral<T>
599 requires std::unsigned_integral<T>
605 constexpr
auto operator|(
const strong_type& rhs)
const& noexcept(noexcept(m_value | rhs.m_value)) ->
strong_type
606 requires std::unsigned_integral<T>
612 constexpr
auto operator|(
strong_type&& rhs)
const& noexcept(noexcept(m_value | rhs.m_value)) ->
strong_type
613 requires std::unsigned_integral<T>
619 constexpr
auto operator|(
const strong_type& rhs) && noexcept(noexcept(m_value | rhs.m_value)) ->
strong_type
620 requires std::unsigned_integral<T>
627 requires std::unsigned_integral<T>
633 constexpr
auto operator^(
const strong_type& rhs)
const& noexcept(noexcept(m_value ^ rhs.m_value)) ->
strong_type
634 requires std::unsigned_integral<T>
640 constexpr
auto operator^(
strong_type&& rhs)
const& noexcept(noexcept(m_value ^ rhs.m_value)) ->
strong_type
641 requires std::unsigned_integral<T>
647 constexpr
auto operator^(
const strong_type& rhs) && noexcept(noexcept(m_value ^ rhs.m_value)) ->
strong_type
648 requires std::unsigned_integral<T>
655 requires std::unsigned_integral<T>
661 constexpr
auto operator<<(
const strong_type& rhs)
const& noexcept(noexcept(m_value << rhs.m_value)) ->
strong_type
662 requires std::unsigned_integral<T>
668 constexpr
auto operator<<(
strong_type&& rhs)
const& noexcept(noexcept(m_value << rhs.m_value)) ->
strong_type
669 requires std::unsigned_integral<T>
675 constexpr
auto operator<<(
const strong_type& rhs) && noexcept(noexcept(m_value << rhs.m_value)) ->
strong_type
676 requires std::unsigned_integral<T>
683 requires std::unsigned_integral<T>
689 constexpr
auto operator>>(
const strong_type& rhs)
const& noexcept(noexcept(m_value >> rhs.m_value)) ->
strong_type
690 requires std::unsigned_integral<T>
696 constexpr
auto operator>>(
strong_type&& rhs)
const& noexcept(noexcept(m_value >> rhs.m_value)) ->
strong_type
697 requires std::unsigned_integral<T>
703 constexpr
auto operator>>(
const strong_type& rhs) && noexcept(noexcept(m_value >> rhs.m_value)) ->
strong_type
704 requires std::unsigned_integral<T>
711 requires std::unsigned_integral<T>
717 constexpr
auto operator&=(
const strong_type& rhs) & noexcept(noexcept(m_value &= rhs.m_value)) ->
strong_type&
718 requires std::unsigned_integral<T>
720 m_value &= rhs.m_value;
726 requires std::unsigned_integral<T>
728 m_value &= rhs.m_value;
733 constexpr
auto operator|=(
const strong_type& rhs) & noexcept(noexcept(m_value |= rhs.m_value)) ->
strong_type&
734 requires std::unsigned_integral<T>
736 m_value |= rhs.m_value;
742 requires std::unsigned_integral<T>
744 m_value |= rhs.m_value;
749 constexpr
auto operator^=(
const strong_type& rhs) & noexcept(noexcept(m_value ^= rhs.m_value)) ->
strong_type&
750 requires std::unsigned_integral<T>
752 m_value ^= rhs.m_value;
758 requires std::unsigned_integral<T>
760 m_value ^= rhs.m_value;
765 constexpr
auto operator<<=(
const strong_type& rhs) & noexcept(noexcept(m_value <<= rhs.m_value)) ->
strong_type&
766 requires std::unsigned_integral<T>
768 m_value <<= rhs.m_value;
773 constexpr
auto operator<<=(
strong_type&& rhs) & noexcept(noexcept(m_value <<= rhs.m_value)) ->
strong_type&
774 requires std::unsigned_integral<T>
776 m_value <<= rhs.m_value;
781 constexpr
auto operator>>=(
const strong_type& rhs) & noexcept(noexcept(m_value >>= rhs.m_value)) ->
strong_type&
782 requires std::unsigned_integral<T>
784 m_value >>= rhs.m_value;
789 constexpr
auto operator>>=(
strong_type&& rhs) & noexcept(noexcept(m_value >>= rhs.m_value)) ->
strong_type&
790 requires std::unsigned_integral<T>
792 m_value >>= rhs.m_value;
801 constexpr
auto begin() const noexcept(noexcept(std::ranges::begin(m_value)))
802 requires std::ranges::range<T>
804 return std::ranges::begin(m_value);
808 constexpr
auto begin() noexcept(noexcept(std::ranges::begin(m_value)))
809 requires std::ranges::range<T>
811 return std::ranges::begin(m_value);
815 constexpr
auto end() const noexcept(noexcept(std::ranges::end(m_value)))
816 requires std::ranges::range<T>
818 return std::ranges::end(m_value);
822 constexpr
auto end() noexcept(noexcept(std::ranges::end(m_value)))
823 requires std::ranges::range<T>
825 return std::ranges::end(m_value);
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>
837 return ostream << rhs.m_value;
841 friend inline auto operator>>(std::istream& istream,
842 strong_type& rhs) noexcept(noexcept(istream >> rhs.m_value)) -> std::istream&
843 requires istreamable<T>
845 return istream >> rhs.m_value;
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...>
861 return strong_type<std::remove_cvref_t<T>, Tag>{std::forward<Args>(args)...};
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...>
870 return strong_type<std::remove_cvref_t<T>, Tag>{ilist, std::forward<Args>(args)...};
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>
879 return strong_type<std::remove_cvref_t<T>, Tag>{std::forward<T>(value)};
891 template <::gw::hashable T, ::gw::complete 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;
906 template <::gw::
string_convertable T,
typename Tag>
908 [[nodiscard]]
auto inline to_string(const ::gw::strong_type<T, Tag>& strong_type) ->
string {
909 return to_string(strong_type.value());
913 template <::gw::
string_convertable T, ::gw::named Tag>
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());
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