1/*
2 * strong_type C++14/17/20 strong typedef library
3 *
4 * Copyright (C) Björn Fahller
5 *
6 * Use, modification and distribution is subject to the
7 * Boost Software License, Version 1.0. (See accompanying
8 * file LICENSE_1_0.txt or copy at
9 * http://www.boost.org/LICENSE_1_0.txt)
10 *
11 * Project home: https://github.com/rollbear/strong_type
12 */
13
14#ifndef ROLLBEAR_STRONG_TYPE_HPP_INCLUDED
15#define ROLLBEAR_STRONG_TYPE_HPP_INCLUDED
16
17#include <functional>
18#include <istream>
19#include <ostream>
20#include <type_traits>
21#include <utility>
22
23#if __cplusplus >= 201703L
24#define STRONG_NODISCARD [[nodiscard]]
25#else
26#define STRONG_NODISCARD
27#endif
28
29#if defined(_MSC_VER) && !defined(__clang__) && __MSC_VER < 1922
30#define STRONG_CONSTEXPR
31#else
32#define STRONG_CONSTEXPR constexpr
33#endif
34
35#ifndef STRONG_HAS_STD_FORMAT
36#define STRONG_HAS_STD_FORMAT 0
37#endif
38
39#ifndef STRONG_HAS_FMT_FORMAT
40#define STRONG_HAS_FMT_FORMAT 0
41#endif
42
43#if STRONG_HAS_STD_FORMAT
44#include <format>
45#if !defined(__cpp_lib_format) || __cpp_lib_format < 201907
46#undef STRONG_HAS_STD_FORMAT
47#define STRONG_HAS_STD_FORMAT 0
48#endif
49#endif
50
51#if STRONG_HAS_FMT_FORMAT
52#include <fmt/format.h>
53#endif
54
55namespace strong
56{
57
58namespace impl
59{
60 template <typename T, typename ... V>
61 using WhenConstructible = std::enable_if_t<std::is_constructible<T, V...>::value>;
62}
63
64template <typename M, typename T>
65using modifier = typename M::template modifier<T>;
66
67struct uninitialized_t {};
68static constexpr uninitialized_t uninitialized{};
69
70struct default_constructible
71{
72 template <typename T>
73 class modifier
74 {
75 };
76};
77
78namespace impl {
79 template <typename T>
80 constexpr bool supports_default_construction(const ::strong::default_constructible::modifier<T>*)
81 {
82 return true;
83 }
84}
85
86template <typename T, typename Tag, typename ... M>
87class type : public modifier<M, type<T, Tag, M...>>...
88{
89public:
90 template <typename TT = T, typename = std::enable_if_t<std::is_trivially_constructible<TT>{}>>
91 explicit type(uninitialized_t)
92 noexcept
93 {
94 }
95 template <typename type_ = type,
96 bool = impl::supports_default_construction(static_cast<type_*>(nullptr))>
97 constexpr
98 type()
99 noexcept(noexcept(T{}))
100 : val{}
101 {
102 }
103
104 template <typename U,
105 typename = impl::WhenConstructible<T, std::initializer_list<U>>>
106 constexpr
107 explicit
108 type(
109 std::initializer_list<U> us
110 )
111 noexcept(noexcept(T{us}))
112 : val{us}
113 {
114 }
115 template <typename ... U,
116 typename = std::enable_if_t<std::is_constructible<T, U&&...>::value && (sizeof...(U) > 0)>>
117 constexpr
118 explicit
119 type(
120 U&& ... u)
121 noexcept(std::is_nothrow_constructible<T, U...>::value)
122 : val(std::forward<U>(u)...)
123 {}
124
125 friend STRONG_CONSTEXPR void swap(type& a, type& b) noexcept(
126 std::is_nothrow_move_constructible<T>::value &&
127 std::is_nothrow_move_assignable<T>::value
128 )
129 {
130 using std::swap;
131 swap(a.val, b.val);
132 }
133
134 STRONG_NODISCARD
135 constexpr T& value_of() & noexcept { return val;}
136 STRONG_NODISCARD
137 constexpr const T& value_of() const & noexcept { return val;}
138 STRONG_NODISCARD
139 constexpr T&& value_of() && noexcept { return std::move(val);}
140
141 STRONG_NODISCARD
142 friend constexpr T& value_of(type& t) noexcept { return t.val;}
143 STRONG_NODISCARD
144 friend constexpr const T& value_of(const type& t) noexcept { return t.val;}
145 STRONG_NODISCARD
146 friend constexpr T&& value_of(type&& t) noexcept { return std::move(t).val;}
147private:
148 T val;
149};
150
151namespace impl {
152 template <typename T, typename Tag, typename ... Ms>
153 constexpr bool is_strong_type_func(const strong::type<T, Tag, Ms...>*) { return true;}
154 constexpr bool is_strong_type_func(...) { return false;}
155 template <typename T, typename Tag, typename ... Ms>
156 constexpr T underlying_type(strong::type<T, Tag, Ms...>*);
157
158}
159
160template <typename T>
161struct is_strong_type : std::integral_constant<bool, impl::is_strong_type_func(static_cast<T *>(nullptr))> {};
162
163namespace impl {
164 template <typename T>
165 using WhenStrongType = std::enable_if_t<is_strong_type<std::decay_t<T>>::value>;
166 template <typename T>
167 using WhenNotStrongType = std::enable_if_t<!is_strong_type<std::decay_t<T>>::value>;
168}
169
170template <typename T, bool = is_strong_type<T>::value>
171struct underlying_type
172{
173 using type = decltype(impl::underlying_type(static_cast<T*>(nullptr)));
174};
175
176template <typename T>
177struct underlying_type<T, false>
178{
179 using type = T;
180};
181
182template <typename T>
183using underlying_type_t = typename underlying_type<T>::type;
184
185
186namespace impl {
187 template<
188 typename T,
189 typename = impl::WhenNotStrongType<T>>
190 constexpr
191 T &&
192 access(T &&t)
193 noexcept {
194 return std::forward<T>(t);
195 }
196 template <
197 typename T,
198 typename = impl::WhenStrongType<T>>
199 STRONG_NODISCARD
200 constexpr
201 auto
202 access(T&& t)
203 noexcept
204 -> decltype(value_of(std::forward<T>(t)))
205 {
206 return value_of(std::forward<T>(t));
207 }
208
209}
210struct equality
211{
212 template <typename T>
213 class modifier;
214};
215
216
217template <typename T, typename Tag, typename ... M>
218class equality::modifier<::strong::type<T, Tag, M...>>
219{
220 using type = ::strong::type<T, Tag, M...>;
221public:
222 STRONG_NODISCARD
223 friend
224 STRONG_CONSTEXPR
225 auto
226 operator==(
227 const type& lh,
228 const type& rh)
229 noexcept(noexcept(std::declval<const T&>() == std::declval<const T&>()))
230 -> decltype(std::declval<const T&>() == std::declval<const T&>())
231 {
232 return value_of(lh) == value_of(rh);
233 }
234
235 STRONG_NODISCARD
236 friend
237 STRONG_CONSTEXPR
238 auto
239 operator!=(
240 const type& lh,
241 const type& rh)
242 noexcept(noexcept(std::declval<const T&>() != std::declval<const T&>()))
243 -> decltype(std::declval<const T&>() != std::declval<const T&>())
244 {
245 return value_of(lh) != value_of(rh);
246 }
247};
248
249namespace impl
250{
251 template <typename T, typename Other>
252 class typed_equality
253 {
254 private:
255 using TT = underlying_type_t<T>;
256 using OT = underlying_type_t<Other>;
257 public:
258 STRONG_NODISCARD
259 friend
260 STRONG_CONSTEXPR
261 auto operator==(const T& lh, const Other& rh)
262 noexcept(noexcept(std::declval<const TT&>() == std::declval<const OT&>()))
263 -> decltype(std::declval<const TT&>() == std::declval<const OT&>())
264 {
265 return value_of(lh) == impl::access(rh);
266 }
267 STRONG_NODISCARD
268 friend
269 STRONG_CONSTEXPR
270 auto operator==(const Other& lh, const T& rh)
271 noexcept(noexcept(std::declval<const OT&>() == std::declval<const TT&>()))
272 -> decltype(std::declval<const OT&>() == std::declval<const TT&>())
273 {
274 return impl::access(lh) == value_of(rh) ;
275 }
276 STRONG_NODISCARD
277 friend
278 STRONG_CONSTEXPR
279 auto operator!=(const T& lh, const Other rh)
280 noexcept(noexcept(std::declval<const TT&>() != std::declval<const OT&>()))
281 -> decltype(std::declval<const TT&>() != std::declval<const OT&>())
282 {
283 return value_of(lh) != impl::access(rh);
284 }
285 STRONG_NODISCARD
286 friend
287 STRONG_CONSTEXPR
288 auto operator!=(const Other& lh, const T& rh)
289 noexcept(noexcept(std::declval<const OT&>() != std::declval<const TT&>()))
290 -> decltype(std::declval<const OT&>() != std::declval<const TT&>())
291 {
292 return impl::access(lh) != value_of(rh) ;
293 }
294 };
295}
296template <typename ... Ts>
297struct equality_with
298{
299 template <typename T>
300 class modifier : public impl::typed_equality<T, Ts>...
301 {
302 };
303};
304
305namespace impl
306{
307 template <typename T, typename Other>
308 class typed_ordering
309 {
310 private:
311 using TT = underlying_type_t<T>;
312 using OT = underlying_type_t<Other>;
313 public:
314 STRONG_NODISCARD
315 friend
316 STRONG_CONSTEXPR
317 auto operator<(const T& lh, const Other& rh)
318 noexcept(noexcept(std::declval<const TT&>() < std::declval<const OT&>()))
319 -> decltype(std::declval<const TT&>() < std::declval<const OT&>())
320 {
321 return value_of(lh) < impl::access(rh);
322 }
323 STRONG_NODISCARD
324 friend
325 STRONG_CONSTEXPR
326 auto operator<(const Other& lh, const T& rh)
327 noexcept(noexcept(std::declval<const OT&>() < std::declval<const TT&>()))
328 -> decltype(std::declval<const OT&>() < std::declval<const TT&>())
329 {
330 return impl::access(lh) < value_of(rh) ;
331 }
332
333 STRONG_NODISCARD
334 friend
335 STRONG_CONSTEXPR
336 auto operator<=(const T& lh, const Other& rh)
337 noexcept(noexcept(std::declval<const TT&>() <= std::declval<const OT&>()))
338 -> decltype(std::declval<const TT&>() <= std::declval<const OT&>())
339 {
340 return value_of(lh) <= impl::access(rh);
341 }
342 STRONG_NODISCARD
343 friend
344 STRONG_CONSTEXPR
345 auto operator<=(const Other& lh, const T& rh)
346 noexcept(noexcept(std::declval<const OT&>() <= std::declval<const TT&>()))
347 -> decltype(std::declval<const OT&>() <= std::declval<const TT&>())
348 {
349 return impl::access(lh) <= value_of(rh) ;
350 }
351
352 STRONG_NODISCARD
353 friend
354 STRONG_CONSTEXPR
355 auto operator>(const T& lh, const Other& rh)
356 noexcept(noexcept(std::declval<const TT&>() > std::declval<const OT&>()))
357 -> decltype(std::declval<const TT&>() > std::declval<const OT&>())
358 {
359 return value_of(lh) > impl::access(rh);
360 }
361 STRONG_NODISCARD
362 friend
363 STRONG_CONSTEXPR
364 auto operator>(const Other& lh, const T& rh)
365 noexcept(noexcept(std::declval<const OT&>() > std::declval<const TT&>()))
366 -> decltype(std::declval<const OT&>() > std::declval<const TT&>())
367 {
368 return impl::access(lh) > value_of(rh) ;
369 }
370
371 STRONG_NODISCARD
372 friend
373 STRONG_CONSTEXPR
374 auto operator>=(const T& lh, const Other& rh)
375 noexcept(noexcept(std::declval<const TT&>() >= std::declval<const OT&>()))
376 -> decltype(std::declval<const TT&>() >= std::declval<const OT&>())
377 {
378 return value_of(lh) >= impl::access(rh);
379 }
380 STRONG_NODISCARD
381 friend
382 STRONG_CONSTEXPR
383 auto operator>=(const Other& lh, const T& rh)
384 noexcept(noexcept(std::declval<const OT&>() >= std::declval<const TT&>()))
385 -> decltype(std::declval<const OT&>() >= std::declval<const TT&>())
386 {
387 return impl::access(lh) >= value_of(rh) ;
388 }
389 };
390}
391
392template <typename ... Ts>
393struct ordered_with
394{
395 template <typename T>
396 class modifier : public impl::typed_ordering<T, Ts>...
397 {
398 };
399};
400
401namespace impl
402{
403 template <typename T>
404 struct require_copy_constructible
405 {
406 static constexpr bool value = std::is_copy_constructible<underlying_type_t<T>>::value;
407 static_assert(value, "underlying type must be copy constructible");
408 };
409 template <typename T>
410 struct require_move_constructible
411 {
412 static constexpr bool value = std::is_move_constructible<underlying_type_t<T>>::value;
413 static_assert(value, "underlying type must be move constructible");
414 };
415 template <typename T>
416 struct require_copy_assignable
417 {
418 static constexpr bool value = std::is_copy_assignable<underlying_type_t<T>>::value;
419 static_assert(value, "underlying type must be copy assignable");
420 };
421 template <typename T>
422 struct require_move_assignable
423 {
424 static constexpr bool value = std::is_move_assignable<underlying_type_t<T>>::value;
425 static_assert(value, "underlying type must be move assignable");
426 };
427
428 template <bool> struct valid_type;
429 template <>
430 struct valid_type<true> {};
431
432 template <typename T>
433 struct require_semiregular
434 : valid_type<require_copy_constructible<T>::value &&
435 require_move_constructible<T>::value &&
436 require_copy_assignable<T>::value &&
437 require_move_assignable<T>::value>
438 {
439 };
440
441}
442struct semiregular
443{
444 template <typename>
445 class modifier;
446};
447
448template <typename T, typename Tag, typename ... M>
449class semiregular::modifier<::strong::type<T, Tag, M...>>
450 : public default_constructible::modifier<T>
451 , private impl::require_semiregular<T>
452{
453};
454
455struct regular
456{
457 template <typename T>
458 class modifier
459 : public semiregular::modifier<T>
460 , public equality::modifier<T>
461 {
462 };
463};
464
465struct unique
466{
467 template <typename T>
468 class modifier
469 : private impl::valid_type<
470 impl::require_move_constructible<T>::value &&
471 impl::require_move_assignable<T>::value
472 >
473 {
474 public:
475 constexpr modifier() = default;
476 modifier(const modifier&) = delete;
477 constexpr modifier(modifier&&) = default;
478 modifier& operator=(const modifier&) = delete;
479 constexpr modifier& operator=(modifier&&) = default;
480 };
481};
482struct ordered
483{
484 template <typename T>
485 class modifier;
486};
487
488
489template <typename T, typename Tag, typename ... M>
490class ordered::modifier<::strong::type<T, Tag, M...>>
491{
492 using type = ::strong::type<T, Tag, M...>;
493public:
494 STRONG_NODISCARD
495 friend
496 STRONG_CONSTEXPR
497 auto
498 operator<(
499 const type& lh,
500 const type& rh)
501 noexcept(noexcept(std::declval<const T&>() < std::declval<const T&>()))
502 -> decltype(std::declval<const T&>() < std::declval<const T&>())
503 {
504 return value_of(lh) < value_of(rh);
505 }
506
507 STRONG_NODISCARD
508 friend
509 STRONG_CONSTEXPR
510 auto
511 operator<=(
512 const type& lh,
513 const type& rh)
514 noexcept(noexcept(std::declval<const T&>() <= std::declval<const T&>()))
515 -> decltype(std::declval<const T&>() <= std::declval<const T&>())
516 {
517 return value_of(lh) <= value_of(rh);
518 }
519
520 STRONG_NODISCARD
521 friend
522 STRONG_CONSTEXPR
523 auto
524 operator>(
525 const type& lh,
526 const type& rh)
527 noexcept(noexcept(std::declval<const T&>() > std::declval<const T&>()))
528 -> decltype(std::declval<const T&>() > std::declval<const T&>())
529 {
530 return value_of(lh) > value_of(rh);
531 }
532
533 STRONG_NODISCARD
534 friend
535 STRONG_CONSTEXPR
536
537 auto
538 operator>=(
539 const type& lh,
540 const type& rh)
541 noexcept(noexcept(std::declval<const T&>() >= std::declval<const T&>()))
542 -> decltype(std::declval<const T&>() >= std::declval<const T&>())
543 {
544 return value_of(lh) >= value_of(rh);
545 }
546};
547
548struct ostreamable
549{
550 template <typename T>
551 class modifier
552 {
553 public:
554 friend
555 std::ostream&
556 operator<<(
557 std::ostream &os,
558 const T &t)
559 {
560 return os << value_of(t);
561 }
562 };
563};
564
565struct istreamable
566{
567 template <typename T>
568 class modifier
569 {
570 public:
571 friend
572 std::istream&
573 operator>>(
574 std::istream &is,
575 T &t)
576 {
577 return is >> value_of(t);
578 }
579 };
580};
581
582struct iostreamable
583{
584 template <typename T>
585 class modifier
586 : public ostreamable::modifier<T>
587 , public istreamable::modifier<T>
588 {
589 };
590};
591
592struct incrementable
593{
594 template <typename T>
595 class modifier
596 {
597 public:
598 friend
599 STRONG_CONSTEXPR
600 T&
601 operator++(T& t)
602 noexcept(noexcept(++std::declval<T&>().value_of()))
603 {
604 ++value_of(t);
605 return t;
606 }
607
608 friend
609 STRONG_CONSTEXPR
610 T
611 operator++(T& t, int)
612 {
613 auto copy = t;
614 ++t;
615 return copy;
616 }
617 };
618};
619
620struct decrementable
621{
622 template <typename T>
623 class modifier
624 {
625 public:
626 friend
627 STRONG_CONSTEXPR
628 T&
629 operator--(T& t)
630 noexcept(noexcept(--std::declval<T&>().value_of()))
631 {
632 --value_of(t);
633 return t;
634 }
635
636 friend
637 STRONG_CONSTEXPR
638 T
639 operator--(T& t, int)
640 {
641 auto copy = t;
642 --t;
643 return copy;
644 }
645 };
646};
647
648struct bicrementable
649{
650 template <typename T>
651 class modifier
652 : public incrementable::modifier<T>
653 , public decrementable::modifier<T>
654 {
655 };
656};
657
658struct boolean
659{
660 template <typename T>
661 class modifier
662 {
663 public:
664 explicit STRONG_CONSTEXPR operator bool() const
665 noexcept(noexcept(static_cast<bool>(value_of(std::declval<const T&>()))))
666 {
667 const auto& self = static_cast<const T&>(*this);
668 return static_cast<bool>(value_of(self));
669 }
670 };
671};
672
673struct hashable
674{
675 template <typename T>
676 class modifier{};
677};
678
679struct difference
680{
681 template <typename T>
682 class modifier;
683};
684
685template <typename T, typename Tag, typename ... M>
686class difference::modifier<::strong::type<T, Tag, M...>>
687: public ordered::modifier<::strong::type<T, Tag, M...>>
688, public equality::modifier<::strong::type<T, Tag, M...>>
689{
690 using type = ::strong::type<T, Tag, M...>;
691public:
692 friend
693 STRONG_CONSTEXPR
694 type& operator+=(type& lh, const type& rh)
695 noexcept(noexcept(value_of(lh) += value_of(rh)))
696 {
697 value_of(lh) += value_of(rh);
698 return lh;
699 }
700
701 friend
702 STRONG_CONSTEXPR
703 type& operator-=(type& lh, const type& rh)
704 noexcept(noexcept(value_of(lh) -= value_of(rh)))
705 {
706 value_of(lh) -= value_of(rh);
707 return lh;
708 }
709
710 friend
711 STRONG_CONSTEXPR
712 type& operator*=(type& lh, const T& rh)
713 noexcept(noexcept(value_of(lh) *= rh))
714 {
715 value_of(lh) *= rh;
716 return lh;
717 }
718
719 friend
720 STRONG_CONSTEXPR
721 type& operator/=(type& lh, const T& rh)
722 noexcept(noexcept(value_of(lh) /= rh))
723 {
724 value_of(lh) /= rh;
725 return lh;
726 }
727
728 template <typename TT = T, typename = decltype(std::declval<TT&>()%= std::declval<const TT&>())>
729 friend
730 STRONG_CONSTEXPR
731 type& operator%=(type& lh, const T& rh)
732 noexcept(noexcept(value_of(lh) %= rh))
733 {
734 value_of(lh)%= rh;
735 return lh;
736 }
737
738 friend
739 STRONG_CONSTEXPR
740 type operator+(type lh, const type& rh)
741 {
742 lh += rh;
743 return lh;
744 }
745
746 friend
747 STRONG_CONSTEXPR
748 type operator-(type lh, const type& rh)
749 {
750 lh -= rh;
751 return lh;
752 }
753
754 friend
755 STRONG_CONSTEXPR
756 type operator*(type lh, const T& rh)
757 {
758 lh *= rh;
759 return lh;
760 }
761
762 friend
763 STRONG_CONSTEXPR
764 type operator*(const T& lh, type rh)
765 {
766 rh *= lh;
767 return rh;
768 }
769
770 friend
771 STRONG_CONSTEXPR
772 type operator/(type lh, const T& rh)
773 {
774 lh /= rh;
775 return lh;
776 }
777
778 friend
779 STRONG_CONSTEXPR
780 T operator/(const type& lh, const type& rh)
781 {
782 return value_of(lh) / value_of(rh);
783 }
784
785 template <typename TT = T, typename = decltype(std::declval<TT&>() %= std::declval<const TT&>())>
786 friend
787 STRONG_CONSTEXPR
788 type operator%(type lh, const T& rh)
789 noexcept(noexcept(lh%= rh))
790 {
791 lh %= rh;
792 return lh;
793 }
794
795 template <typename TT = T, typename = decltype(std::declval<TT>() % std::declval<TT>())>
796 friend
797 STRONG_CONSTEXPR
798 T operator%(type lh, type rh)
799 noexcept(noexcept(value_of(lh) % value_of(rh)))
800 {
801 return value_of(lh) % value_of(rh);
802 }
803};
804
805template <typename D = void>
806struct affine_point
807{
808 template <typename T>
809 class modifier;
810};
811
812namespace impl
813{
814 template <typename ...>
815 using void_t = void;
816
817 template <typename T, typename = void>
818 struct subtractable : std::false_type {};
819
820 template <typename T>
821 struct subtractable<T, void_t<decltype(std::declval<const T&>() - std::declval<const T&>())>>
822 : std::true_type {};
823}
824
825
826template <typename D>
827template <typename T, typename Tag, typename ... M>
828class affine_point<D>::modifier<::strong::type<T, Tag, M...>>
829{
830 using type = ::strong::type<T, Tag, M...>;
831 static_assert(impl::subtractable<T>::value, "it must be possible to subtract instances of your underlying type");
832 using base_diff_type = decltype(std::declval<const T&>() - std::declval<const T&>());
833public:
834 using difference = std::conditional_t<std::is_same<D, void>{}, strong::type<base_diff_type, Tag, strong::difference>, D>;
835 static_assert(std::is_constructible<difference, base_diff_type>::value, "");
836 STRONG_NODISCARD
837 friend
838 STRONG_CONSTEXPR
839 difference
840 operator-(
841 const type& lh,
842 const type& rh)
843 {
844 return difference(value_of(lh) - value_of(rh));
845 }
846
847 friend
848 STRONG_CONSTEXPR
849 type&
850 operator+=(
851 type& lh,
852 const difference& d)
853 noexcept(noexcept(value_of(lh) += impl::access(d)))
854 {
855 value_of(lh) += impl::access(d);
856 return lh;
857 }
858
859 friend
860 STRONG_CONSTEXPR
861 type&
862 operator-=(
863 type& lh,
864 const difference& d)
865 noexcept(noexcept(value_of(lh) -= impl::access(d)))
866 {
867 value_of(lh) -= impl::access(d);
868 return lh;
869 }
870
871 STRONG_NODISCARD
872 friend
873 STRONG_CONSTEXPR
874 type
875 operator+(
876 type lh,
877 const difference& d)
878 {
879 return lh += d;
880 }
881
882 STRONG_NODISCARD
883 friend
884 STRONG_CONSTEXPR
885 type
886 operator+(
887 const difference& d,
888 type rh)
889 {
890 return rh+= d;
891 }
892
893 STRONG_NODISCARD
894 friend
895 STRONG_CONSTEXPR
896 type
897 operator-(
898 type lh,
899 const difference& d)
900 {
901 return lh -= d;
902 }
903};
904
905
906struct pointer
907{
908 template <typename T>
909 class modifier;
910};
911
912template <typename T, typename Tag, typename ... M>
913class pointer::modifier<::strong::type<T, Tag, M...>>
914{
915 using type = strong::type<T, Tag, M...>;
916public:
917 template <typename TT = T>
918 STRONG_NODISCARD
919 friend
920 STRONG_CONSTEXPR
921 auto
922 operator==(
923 const type& t,
924 std::nullptr_t)
925 noexcept(noexcept(std::declval<const TT&>() == nullptr))
926 -> decltype(std::declval<const TT&>() == nullptr)
927 {
928 return value_of(t) == nullptr;
929 }
930
931 template <typename TT = T>
932 STRONG_NODISCARD
933 friend
934 STRONG_CONSTEXPR
935 auto
936 operator==(
937 std::nullptr_t,
938 const type& t)
939 noexcept(noexcept(nullptr == std::declval<const TT&>()))
940 -> decltype(nullptr == std::declval<const TT&>())
941 {
942 return value_of(t) == nullptr;
943 }
944
945 template <typename TT = T>
946 STRONG_NODISCARD
947 friend
948 STRONG_CONSTEXPR
949 auto
950 operator!=(
951 const type& t,
952 std::nullptr_t)
953 noexcept(noexcept(std::declval<const TT&>() != nullptr))
954 -> decltype(std::declval<const TT&>() != nullptr)
955 {
956 return value_of(t) != nullptr;
957 }
958
959 template <typename TT = T>
960 STRONG_NODISCARD
961 friend
962 STRONG_CONSTEXPR
963 auto
964 operator!=(
965 std::nullptr_t,
966 const type& t)
967 noexcept(noexcept(nullptr != std::declval<const TT&>()))
968 -> decltype(nullptr != std::declval<const TT&>())
969 {
970 return value_of(t) != nullptr;
971 }
972
973 STRONG_NODISCARD
974 STRONG_CONSTEXPR
975 decltype(*std::declval<const T&>())
976 operator*()
977 const
978 {
979 auto& self = static_cast<const type&>(*this);
980 return *value_of(self);
981 }
982
983 STRONG_NODISCARD
984 STRONG_CONSTEXPR
985 decltype(&(*std::declval<const T&>())) operator->() const { return &operator*();}
986};
987
988struct arithmetic
989{
990 template <typename T>
991 class modifier
992 {
993 public:
994 STRONG_NODISCARD
995 friend
996 STRONG_CONSTEXPR
997 T
998 operator-(
999 const T &lh)
1000 {
1001 return T{-value_of(lh)};
1002 }
1003
1004 friend
1005 STRONG_CONSTEXPR
1006 T&
1007 operator+=(
1008 T &lh,
1009 const T &rh)
1010 noexcept(noexcept(value_of(lh) += value_of(rh)))
1011 {
1012 value_of(lh) += value_of(rh);
1013 return lh;
1014 }
1015
1016 friend
1017 STRONG_CONSTEXPR
1018 T&
1019 operator-=(
1020 T &lh,
1021 const T &rh)
1022 noexcept(noexcept(value_of(lh) -= value_of(rh)))
1023 {
1024 value_of(lh) -= value_of(rh);
1025 return lh;
1026 }
1027
1028 friend
1029 STRONG_CONSTEXPR
1030 T&
1031 operator*=(
1032 T &lh,
1033 const T &rh)
1034 noexcept(noexcept(value_of(lh) *= value_of(rh)))
1035 {
1036 value_of(lh) *= value_of(rh);
1037 return lh;
1038 }
1039
1040 friend
1041 STRONG_CONSTEXPR
1042 T&
1043 operator/=(
1044 T &lh,
1045 const T &rh)
1046 noexcept(noexcept(value_of(lh) /= value_of(rh)))
1047 {
1048 value_of(lh) /= value_of(rh);
1049 return lh;
1050 }
1051
1052 template <typename TT = T, typename = decltype(value_of(std::declval<TT>()) % value_of(std::declval<TT>()))>
1053 friend
1054 STRONG_CONSTEXPR
1055 T&
1056 operator%=(
1057 T &lh,
1058 const T &rh)
1059 noexcept(noexcept(value_of(lh) %= value_of(rh)))
1060 {
1061 value_of(lh) %= value_of(rh);
1062 return lh;
1063 }
1064
1065 STRONG_NODISCARD
1066 friend
1067 STRONG_CONSTEXPR
1068 T
1069 operator+(
1070 T lh,
1071 const T &rh)
1072 {
1073 lh += rh;
1074 return lh;
1075 }
1076
1077 STRONG_NODISCARD
1078 friend
1079 STRONG_CONSTEXPR
1080 T
1081 operator-(
1082 T lh,
1083 const T &rh)
1084 {
1085 lh -= rh;
1086 return lh;
1087 }
1088
1089 STRONG_NODISCARD
1090 friend
1091 STRONG_CONSTEXPR
1092 T
1093 operator*(
1094 T lh,
1095 const T &rh)
1096 {
1097 lh *= rh;
1098 return lh;
1099 }
1100
1101 STRONG_NODISCARD
1102 friend
1103 STRONG_CONSTEXPR
1104 T
1105 operator/(
1106 T lh,
1107 const T &rh)
1108 {
1109 lh /= rh;
1110 return lh;
1111 }
1112
1113 template <typename TT = T, typename = decltype(value_of(std::declval<TT>()) % value_of(std::declval<TT>()))>
1114 STRONG_NODISCARD
1115 friend
1116 STRONG_CONSTEXPR
1117 T
1118 operator%(
1119 T lh,
1120 const T &rh)
1121 {
1122 lh %= rh;
1123 return lh;
1124 }
1125
1126 };
1127};
1128
1129
1130struct bitarithmetic
1131{
1132 template <typename T>
1133 class modifier
1134 {
1135 public:
1136 friend
1137 STRONG_CONSTEXPR
1138 T&
1139 operator&=(
1140 T &lh,
1141 const T &rh)
1142 noexcept(noexcept(value_of(lh) &= value_of(rh)))
1143 {
1144 value_of(lh) &= value_of(rh);
1145 return lh;
1146 }
1147
1148 friend
1149 STRONG_CONSTEXPR
1150 T&
1151 operator|=(
1152 T &lh,
1153 const T &rh)
1154 noexcept(noexcept(value_of(lh) |= value_of(rh)))
1155 {
1156 value_of(lh) |= value_of(rh);
1157 return lh;
1158 }
1159
1160 friend
1161 STRONG_CONSTEXPR
1162 T&
1163 operator^=(
1164 T &lh,
1165 const T &rh)
1166 noexcept(noexcept(value_of(lh) ^= value_of(rh)))
1167 {
1168 value_of(lh) ^= value_of(rh);
1169 return lh;
1170 }
1171
1172 template <typename C>
1173 friend
1174 STRONG_CONSTEXPR
1175 T&
1176 operator<<=(
1177 T &lh,
1178 C c)
1179 noexcept(noexcept(value_of(lh) <<= c))
1180 {
1181 value_of(lh) <<= c;
1182 return lh;
1183 }
1184
1185 template <typename C>
1186 friend
1187 STRONG_CONSTEXPR
1188 T&
1189 operator>>=(
1190 T &lh,
1191 C c)
1192 noexcept(noexcept(value_of(lh) >>= c))
1193 {
1194 value_of(lh) >>= c;
1195 return lh;
1196 }
1197
1198 STRONG_NODISCARD
1199 friend
1200 STRONG_CONSTEXPR
1201 T
1202 operator~(
1203 const T &lh)
1204 {
1205 auto v = value_of(lh);
1206 v = ~v;
1207 return T(v);
1208 }
1209
1210 STRONG_NODISCARD
1211 friend
1212 STRONG_CONSTEXPR
1213 T
1214 operator&(
1215 T lh,
1216 const T &rh)
1217 {
1218 lh &= rh;
1219 return lh;
1220 }
1221
1222 STRONG_NODISCARD
1223 friend
1224 STRONG_CONSTEXPR
1225 T
1226 operator|(
1227 T lh,
1228 const T &rh)
1229 {
1230 lh |= rh;
1231 return lh;
1232 }
1233
1234 STRONG_NODISCARD
1235 friend
1236 STRONG_CONSTEXPR
1237 T
1238 operator^(
1239 T lh,
1240 const T &rh)
1241 {
1242 lh ^= rh;
1243 return lh;
1244 }
1245
1246 template <typename C>
1247 STRONG_NODISCARD
1248 friend
1249 STRONG_CONSTEXPR
1250 T
1251 operator<<(
1252 T lh,
1253 C c)
1254 {
1255 lh <<= c;
1256 return lh;
1257 }
1258
1259 template <typename C>
1260 STRONG_NODISCARD
1261 friend
1262 STRONG_CONSTEXPR
1263 T
1264 operator>>(
1265 T lh,
1266 C c)
1267 {
1268 lh >>= c;
1269 return lh;
1270 }
1271 };
1272};
1273template <typename I = void>
1274struct indexed
1275{
1276 template <typename T>
1277 class modifier;
1278};
1279
1280template <>
1281struct indexed<void> {
1282 template<typename>
1283 class modifier;
1284
1285 template <typename T, typename Tag, typename ... Ms>
1286 class modifier<type<T, Tag, Ms...>> {
1287 using ref = T&;
1288 using cref = const T&;
1289 using rref = T&&;
1290 using type = strong::type<T, Tag, Ms...>;
1291 public:
1292 template<typename I>
1293 STRONG_NODISCARD
1294 auto
1295 operator[](
1296 const I &i)
1297 const &
1298 noexcept(noexcept(std::declval<cref>()[impl::access(i)]))
1299 -> decltype(std::declval<cref>()[impl::access(i)]) {
1300 auto& self = static_cast<const type&>(*this);
1301 return value_of(self)[impl::access(i)];
1302 }
1303
1304 template<typename I>
1305 STRONG_NODISCARD
1306 auto
1307 operator[](
1308 const I &i)
1309 &
1310 noexcept(noexcept(std::declval<ref>()[impl::access(i)]))
1311 -> decltype(std::declval<ref>()[impl::access(i)]) {
1312 auto& self = static_cast<type&>(*this);
1313 return value_of(self)[impl::access(i)];
1314 }
1315
1316 template<typename I>
1317 STRONG_NODISCARD
1318 auto
1319 operator[](
1320 const I &i)
1321 &&
1322 noexcept(noexcept(std::declval<rref>()[impl::access(i)]))
1323 -> decltype(std::declval<rref>()[impl::access(i)]) {
1324 auto& self = static_cast<type&>(*this);
1325 return value_of(std::move(self))[impl::access(i)];
1326 }
1327
1328 template<typename I, typename C = cref>
1329 STRONG_NODISCARD
1330 auto
1331 at(
1332 const I &i)
1333 const &
1334 -> decltype(std::declval<C>().at(impl::access(i))) {
1335 auto& self = static_cast<const type&>(*this);
1336 return value_of(self).at(impl::access(i));
1337 }
1338
1339 template<typename I, typename R = ref>
1340 STRONG_NODISCARD
1341 auto
1342 at(
1343 const I &i)
1344 &
1345 -> decltype(std::declval<R>().at(impl::access(i))) {
1346 auto& self = static_cast<type&>(*this);
1347 return value_of(self).at(impl::access(i));
1348 }
1349
1350 template<typename I, typename R = rref>
1351 STRONG_NODISCARD
1352 auto
1353 at(
1354 const I &i)
1355 &&
1356 -> decltype(std::declval<R>().at(impl::access(i))) {
1357 auto& self = static_cast<type&>(*this);
1358 return value_of(std::move(self)).at(impl::access(i));
1359 }
1360 };
1361};
1362
1363template <typename I>
1364template <typename T, typename Tag, typename ... M>
1365class indexed<I>::modifier<type<T, Tag, M...>>
1366{
1367 using type = ::strong::type<T, Tag, M...>;
1368public:
1369 STRONG_NODISCARD
1370 auto
1371 operator[](
1372 const I& i)
1373 const &
1374 noexcept(noexcept(std::declval<const T&>()[impl::access(i)]))
1375 -> decltype(std::declval<const T&>()[impl::access(i)])
1376 {
1377 auto& self = static_cast<const type&>(*this);
1378 return value_of(self)[impl::access(i)];
1379 }
1380
1381 STRONG_NODISCARD
1382 auto
1383 operator[](
1384 const I& i)
1385 &
1386 noexcept(noexcept(std::declval<T&>()[impl::access(i)]))
1387 -> decltype(std::declval<T&>()[impl::access(i)])
1388 {
1389 auto& self = static_cast<type&>(*this);
1390 return value_of(self)[impl::access(i)];
1391 }
1392
1393 STRONG_NODISCARD
1394 auto
1395 operator[](
1396 const I& i)
1397 &&
1398 noexcept(noexcept(std::declval<T&&>()[impl::access(i)]))
1399 -> decltype(std::declval<T&&>()[impl::access(i)])
1400 {
1401 auto& self = static_cast<type&>(*this);
1402 return value_of(std::move(self))[impl::access(i)];
1403 }
1404
1405 template <typename TT = T>
1406 STRONG_NODISCARD
1407 auto
1408 at(
1409 const I& i)
1410 const &
1411 -> decltype(std::declval<const TT&>().at(impl::access(i)))
1412 {
1413 auto& self = static_cast<const type&>(*this);
1414 return value_of(self).at(impl::access(i));
1415 }
1416
1417 template <typename TT = T>
1418 STRONG_NODISCARD
1419 auto
1420 at(
1421 const I& i)
1422 &
1423 -> decltype(std::declval<TT&>().at(impl::access(i)))
1424 {
1425 auto& self = static_cast<type&>(*this);
1426 return value_of(self).at(impl::access(i));
1427 }
1428
1429 template <typename TT = T>
1430 STRONG_NODISCARD
1431 auto
1432 at(
1433 const I& i)
1434 &&
1435 -> decltype(std::declval<TT&&>().at(impl::access(i)))
1436 {
1437 auto& self = static_cast<type&>(*this);
1438 return value_of(std::move(self)).at(impl::access(i));
1439 }
1440};
1441
1442class iterator
1443{
1444public:
1445 template <typename I, typename category = typename std::iterator_traits<underlying_type_t<I>>::iterator_category>
1446 class modifier
1447 : public pointer::modifier<I>
1448 , public equality::modifier<I>
1449 , public incrementable::modifier<I>
1450 {
1451 public:
1452 using difference_type = typename std::iterator_traits<underlying_type_t<I>>::difference_type;
1453 using value_type = typename std::iterator_traits<underlying_type_t<I>>::value_type;
1454 using pointer = typename std::iterator_traits<underlying_type_t<I>>::value_type;
1455 using reference = typename std::iterator_traits<underlying_type_t<I>>::reference;
1456 using iterator_category = typename std::iterator_traits<underlying_type_t<I>>::iterator_category;
1457 };
1458
1459 template <typename I>
1460 class modifier<I, std::bidirectional_iterator_tag>
1461 : public modifier<I, std::forward_iterator_tag>
1462 , public decrementable::modifier<I>
1463 {
1464 };
1465 template <typename I>
1466 class modifier<I, std::random_access_iterator_tag>
1467 : public modifier<I, std::bidirectional_iterator_tag>
1468 , public affine_point<typename std::iterator_traits<underlying_type_t<I>>::difference_type>::template modifier<I>
1469 , public indexed<>::modifier<I>
1470 , public ordered::modifier<I>
1471 {
1472 };
1473};
1474
1475class range
1476{
1477public:
1478 template <typename R>
1479 class modifier;
1480};
1481
1482template <typename T, typename Tag, typename ... M>
1483class range::modifier<type<T, Tag, M...>>
1484{
1485 using type = ::strong::type<T, Tag, M...>;
1486 using r_iterator = decltype(std::declval<T&>().begin());
1487 using r_const_iterator = decltype(std::declval<const T&>().begin());
1488public:
1489 using iterator = ::strong::type<r_iterator, Tag, strong::iterator>;
1490 using const_iterator = ::strong::type<r_const_iterator, Tag, strong::iterator>;
1491
1492 iterator
1493 begin()
1494 noexcept(noexcept(std::declval<T&>().begin()))
1495 {
1496 auto& self = static_cast<type&>(*this);
1497 return iterator{value_of(self).begin()};
1498 }
1499
1500 iterator
1501 end()
1502 noexcept(noexcept(std::declval<T&>().end()))
1503 {
1504 auto& self = static_cast<type&>(*this);
1505 return iterator{value_of(self).end()};
1506 }
1507
1508 const_iterator
1509 cbegin()
1510 const
1511 noexcept(noexcept(std::declval<const T&>().begin()))
1512 {
1513 auto& self = static_cast<const type&>(*this);
1514 return const_iterator{value_of(self).begin()};
1515 }
1516
1517 const_iterator
1518 cend()
1519 const
1520 noexcept(noexcept(std::declval<const T&>().end()))
1521 {
1522 auto& self = static_cast<const type&>(*this);
1523 return const_iterator{value_of(self).end()};
1524 }
1525
1526 const_iterator
1527 begin()
1528 const
1529 noexcept(noexcept(std::declval<const T&>().begin()))
1530 {
1531 auto& self = static_cast<const type&>(*this);
1532 return const_iterator{value_of(self).begin()};
1533 }
1534
1535 const_iterator
1536 end()
1537 const
1538 noexcept(noexcept(std::declval<const T&>().end()))
1539 {
1540 auto& self = static_cast<const type&>(*this);
1541 return const_iterator{value_of(self).end()};
1542 }
1543};
1544
1545namespace impl {
1546
1547 template<typename T, typename D>
1548 struct converter
1549 {
1550 STRONG_CONSTEXPR explicit operator D() const
1551 noexcept(noexcept(static_cast<D>(std::declval<const underlying_type_t<T>&>())))
1552 {
1553 auto& self = static_cast<const T&>(*this);
1554 return static_cast<D>(value_of(self));
1555 }
1556 };
1557 template<typename T, typename D>
1558 struct implicit_converter
1559 {
1560 STRONG_CONSTEXPR operator D() const
1561 noexcept(noexcept(static_cast<D>(std::declval<const underlying_type_t<T>&>())))
1562 {
1563 auto& self = static_cast<const T&>(*this);
1564 return static_cast<D>(value_of(self));
1565 }
1566 };
1567}
1568template <typename ... Ts>
1569struct convertible_to
1570{
1571 template <typename T>
1572 struct modifier : impl::converter<T, Ts>...
1573 {
1574 };
1575};
1576
1577template <typename ... Ts>
1578struct implicitly_convertible_to
1579{
1580 template <typename T>
1581 struct modifier : impl::implicit_converter<T, Ts>...
1582 {
1583 };
1584
1585};
1586
1587struct formattable
1588{
1589 template <typename T>
1590 class modifier{};
1591};
1592
1593}
1594
1595namespace std {
1596template <typename T, typename Tag, typename ... M>
1597struct hash<::strong::type<T, Tag, M...>>
1598 : std::conditional_t<
1599 std::is_base_of<
1600 ::strong::hashable::modifier<
1601 ::strong::type<T, Tag, M...>
1602 >,
1603 ::strong::type<T, Tag, M...>
1604 >::value,
1605 hash<T>,
1606 std::false_type>
1607{
1608 using type = ::strong::type<T, Tag, M...>;
1609 decltype(auto)
1610 operator()(
1611 const ::strong::hashable::modifier<type>& t)
1612 const
1613 noexcept(noexcept(std::declval<hash<T>>()(value_of(std::declval<const type&>()))))
1614 {
1615 auto& tt = static_cast<const type&>(t);
1616 return hash<T>::operator()(value_of(tt));
1617 }
1618};
1619template <typename T, typename Tag, typename ... M>
1620struct is_arithmetic<::strong::type<T, Tag, M...>>
1621 : is_base_of<::strong::arithmetic::modifier<::strong::type<T, Tag, M...>>,
1622 ::strong::type<T, Tag, M...>>
1623{
1624};
1625
1626#if STRONG_HAS_STD_FORMAT
1627template<typename T, typename Tag, typename... M, typename Char>
1628struct formatter<::strong::type<T, Tag, M...>, Char,
1629 std::enable_if_t<
1630 std::is_base_of<
1631 ::strong::formattable::modifier<
1632 ::strong::type<T, Tag, M...>
1633 >,
1634 ::strong::type<T, Tag, M...>
1635 >::value
1636 >>
1637 : formatter<T>
1638{
1639 using type = ::strong::type<T, Tag, M...>;
1640 template<typename FormatContext>
1641 STRONG_CONSTEXPR
1642 decltype(auto)
1643 format(const ::strong::formattable::modifier<type>& t, FormatContext& fc)
1644 noexcept(noexcept(std::declval<formatter<T, Char>>().format(value_of(std::declval<const type&>()), fc)))
1645 {
1646 const auto& tt = static_cast<const type&>(t);
1647 return formatter<T, Char>::format(value_of(tt), fc);
1648 }
1649};
1650#endif
1651
1652}
1653
1654#if STRONG_HAS_FMT_FORMAT
1655namespace fmt
1656{
1657template<typename T, typename Tag, typename... M, typename Char>
1658struct formatter<::strong::type<T, Tag, M...>, Char,
1659 std::enable_if_t<
1660 std::is_base_of<
1661 ::strong::formattable::modifier<
1662 ::strong::type<T, Tag, M...>
1663 >,
1664 ::strong::type<T, Tag, M...>
1665 >::value
1666 >>
1667 : formatter<T>
1668{
1669 using type = ::strong::type<T, Tag, M...>;
1670 template<typename FormatContext>
1671 STRONG_CONSTEXPR
1672 decltype(auto)
1673 format(const ::strong::formattable::modifier<type>& t, FormatContext& fc)
1674 noexcept(noexcept(std::declval<formatter<T, Char>>().format(value_of(std::declval<const type&>()), fc)))
1675 {
1676 const auto& tt = static_cast<const type&>(t);
1677 return formatter<T, Char>::format(value_of(tt), fc);
1678 }
1679};
1680}
1681#endif
1682#endif //ROLLBEAR_STRONG_TYPE_HPP_INCLUDED
1683