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 | |
55 | namespace strong |
56 | { |
57 | |
58 | namespace impl |
59 | { |
60 | template <typename T, typename ... V> |
61 | using WhenConstructible = std::enable_if_t<std::is_constructible<T, V...>::value>; |
62 | } |
63 | |
64 | template <typename M, typename T> |
65 | using modifier = typename M::template modifier<T>; |
66 | |
67 | struct uninitialized_t {}; |
68 | static constexpr uninitialized_t uninitialized{}; |
69 | |
70 | struct default_constructible |
71 | { |
72 | template <typename T> |
73 | class modifier |
74 | { |
75 | }; |
76 | }; |
77 | |
78 | namespace impl { |
79 | template <typename T> |
80 | constexpr bool supports_default_construction(const ::strong::default_constructible::modifier<T>*) |
81 | { |
82 | return true; |
83 | } |
84 | } |
85 | |
86 | template <typename T, typename Tag, typename ... M> |
87 | class type : public modifier<M, type<T, Tag, M...>>... |
88 | { |
89 | public: |
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;} |
147 | private: |
148 | T val; |
149 | }; |
150 | |
151 | namespace 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 | |
160 | template <typename T> |
161 | struct is_strong_type : std::integral_constant<bool, impl::is_strong_type_func(static_cast<T *>(nullptr))> {}; |
162 | |
163 | namespace 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 | |
170 | template <typename T, bool = is_strong_type<T>::value> |
171 | struct underlying_type |
172 | { |
173 | using type = decltype(impl::underlying_type(static_cast<T*>(nullptr))); |
174 | }; |
175 | |
176 | template <typename T> |
177 | struct underlying_type<T, false> |
178 | { |
179 | using type = T; |
180 | }; |
181 | |
182 | template <typename T> |
183 | using underlying_type_t = typename underlying_type<T>::type; |
184 | |
185 | |
186 | namespace 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 | } |
210 | struct equality |
211 | { |
212 | template <typename T> |
213 | class modifier; |
214 | }; |
215 | |
216 | |
217 | template <typename T, typename Tag, typename ... M> |
218 | class equality::modifier<::strong::type<T, Tag, M...>> |
219 | { |
220 | using type = ::strong::type<T, Tag, M...>; |
221 | public: |
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 | |
249 | namespace 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 | } |
296 | template <typename ... Ts> |
297 | struct equality_with |
298 | { |
299 | template <typename T> |
300 | class modifier : public impl::typed_equality<T, Ts>... |
301 | { |
302 | }; |
303 | }; |
304 | |
305 | namespace 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 | |
392 | template <typename ... Ts> |
393 | struct ordered_with |
394 | { |
395 | template <typename T> |
396 | class modifier : public impl::typed_ordering<T, Ts>... |
397 | { |
398 | }; |
399 | }; |
400 | |
401 | namespace 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 | } |
442 | struct semiregular |
443 | { |
444 | template <typename> |
445 | class modifier; |
446 | }; |
447 | |
448 | template <typename T, typename Tag, typename ... M> |
449 | class semiregular::modifier<::strong::type<T, Tag, M...>> |
450 | : public default_constructible::modifier<T> |
451 | , private impl::require_semiregular<T> |
452 | { |
453 | }; |
454 | |
455 | struct regular |
456 | { |
457 | template <typename T> |
458 | class modifier |
459 | : public semiregular::modifier<T> |
460 | , public equality::modifier<T> |
461 | { |
462 | }; |
463 | }; |
464 | |
465 | struct 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 | }; |
482 | struct ordered |
483 | { |
484 | template <typename T> |
485 | class modifier; |
486 | }; |
487 | |
488 | |
489 | template <typename T, typename Tag, typename ... M> |
490 | class ordered::modifier<::strong::type<T, Tag, M...>> |
491 | { |
492 | using type = ::strong::type<T, Tag, M...>; |
493 | public: |
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 | |
548 | struct 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 | |
565 | struct 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 | |
582 | struct iostreamable |
583 | { |
584 | template <typename T> |
585 | class modifier |
586 | : public ostreamable::modifier<T> |
587 | , public istreamable::modifier<T> |
588 | { |
589 | }; |
590 | }; |
591 | |
592 | struct 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 | |
620 | struct 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 | |
648 | struct bicrementable |
649 | { |
650 | template <typename T> |
651 | class modifier |
652 | : public incrementable::modifier<T> |
653 | , public decrementable::modifier<T> |
654 | { |
655 | }; |
656 | }; |
657 | |
658 | struct 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 | |
673 | struct hashable |
674 | { |
675 | template <typename T> |
676 | class modifier{}; |
677 | }; |
678 | |
679 | struct difference |
680 | { |
681 | template <typename T> |
682 | class modifier; |
683 | }; |
684 | |
685 | template <typename T, typename Tag, typename ... M> |
686 | class 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...>; |
691 | public: |
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 | |
805 | template <typename D = void> |
806 | struct affine_point |
807 | { |
808 | template <typename T> |
809 | class modifier; |
810 | }; |
811 | |
812 | namespace 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 | |
826 | template <typename D> |
827 | template <typename T, typename Tag, typename ... M> |
828 | class 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&>()); |
833 | public: |
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 | |
906 | struct pointer |
907 | { |
908 | template <typename T> |
909 | class modifier; |
910 | }; |
911 | |
912 | template <typename T, typename Tag, typename ... M> |
913 | class pointer::modifier<::strong::type<T, Tag, M...>> |
914 | { |
915 | using type = strong::type<T, Tag, M...>; |
916 | public: |
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 | |
988 | struct 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 | |
1130 | struct 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 | }; |
1273 | template <typename I = void> |
1274 | struct indexed |
1275 | { |
1276 | template <typename T> |
1277 | class modifier; |
1278 | }; |
1279 | |
1280 | template <> |
1281 | struct 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 | |
1363 | template <typename I> |
1364 | template <typename T, typename Tag, typename ... M> |
1365 | class indexed<I>::modifier<type<T, Tag, M...>> |
1366 | { |
1367 | using type = ::strong::type<T, Tag, M...>; |
1368 | public: |
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 | |
1442 | class iterator |
1443 | { |
1444 | public: |
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 | |
1475 | class range |
1476 | { |
1477 | public: |
1478 | template <typename R> |
1479 | class modifier; |
1480 | }; |
1481 | |
1482 | template <typename T, typename Tag, typename ... M> |
1483 | class 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()); |
1488 | public: |
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 | |
1545 | namespace 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 | } |
1568 | template <typename ... Ts> |
1569 | struct convertible_to |
1570 | { |
1571 | template <typename T> |
1572 | struct modifier : impl::converter<T, Ts>... |
1573 | { |
1574 | }; |
1575 | }; |
1576 | |
1577 | template <typename ... Ts> |
1578 | struct implicitly_convertible_to |
1579 | { |
1580 | template <typename T> |
1581 | struct modifier : impl::implicit_converter<T, Ts>... |
1582 | { |
1583 | }; |
1584 | |
1585 | }; |
1586 | |
1587 | struct formattable |
1588 | { |
1589 | template <typename T> |
1590 | class modifier{}; |
1591 | }; |
1592 | |
1593 | } |
1594 | |
1595 | namespace std { |
1596 | template <typename T, typename Tag, typename ... M> |
1597 | struct 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 | }; |
1619 | template <typename T, typename Tag, typename ... M> |
1620 | struct 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 |
1627 | template<typename T, typename Tag, typename... M, typename Char> |
1628 | struct 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 |
1655 | namespace fmt |
1656 | { |
1657 | template<typename T, typename Tag, typename... M, typename Char> |
1658 | struct 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 | |