1 // Copyright (C) 2016-2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
2 // This file is subject to the license terms in the LICENSE file
3 // found in the top-level directory of this distribution.
4
5 #ifndef TYPE_SAFE_INTEGER_HPP_INCLUDED
6 #define TYPE_SAFE_INTEGER_HPP_INCLUDED
7
8 #include <functional>
9 #include <iosfwd>
10 #include <limits>
11 #include <type_traits>
12
13 #include <type_safe/arithmetic_policy.hpp>
14 #include <type_safe/detail/assert.hpp>
15 #include <type_safe/detail/force_inline.hpp>
16
17 namespace type_safe
18 {
19 template <typename IntegerT, class Policy = arithmetic_policy_default>
20 class integer;
21
22 /// \exclude
23 namespace detail
24 {
25 template <typename T>
26 struct is_integer
27 : std::integral_constant<bool, std::is_integral<T>::value && !std::is_same<T, bool>::value
28 && !std::is_same<T, char>::value>
29 {};
30
31 template <typename From, typename To>
32 struct is_safe_integer_conversion
33 : std::integral_constant<bool,
34 detail::is_integer<From>::value && detail::is_integer<To>::value
35 && ((sizeof(From) <= sizeof(To)
36 && std::is_signed<From>::value == std::is_signed<To>::value)
37 || (sizeof(From) < sizeof(To) && std::is_unsigned<From>::value
38 && std::is_signed<To>::value))>
39 {};
40
41 template <typename From, typename To>
42 using enable_safe_integer_conversion =
43 typename std::enable_if<is_safe_integer_conversion<From, To>::value>::type;
44
45 template <typename From, typename To>
46 using fallback_safe_integer_conversion =
47 typename std::enable_if<!is_safe_integer_conversion<From, To>::value>::type;
48
49 template <typename A, typename B>
50 struct is_safe_integer_comparison
51 : std::integral_constant<bool, is_safe_integer_conversion<A, B>::value
52 || is_safe_integer_conversion<B, A>::value>
53 {};
54
55 template <typename A, typename B>
56 using enable_safe_integer_comparison =
57 typename std::enable_if<is_safe_integer_comparison<A, B>::value>::type;
58
59 template <typename A, typename B>
60 using fallback_safe_integer_comparison =
61 typename std::enable_if<!is_safe_integer_comparison<A, B>::value>::type;
62
63 template <typename A, typename B>
64 struct is_safe_integer_operation
65 : std::integral_constant<bool, detail::is_integer<A>::value && detail::is_integer<B>::value
66 && std::is_signed<A>::value == std::is_signed<B>::value>
67 {};
68
69 template <typename A, typename B>
70 struct integer_result_type
71 : std::enable_if<is_safe_integer_operation<A, B>::value,
72 typename std::conditional<sizeof(A) < sizeof(B), B, A>::type>
73 {};
74
75 template <typename A, typename B>
76 using integer_result_t = typename integer_result_type<A, B>::type;
77
78 template <typename A, typename B>
79 using fallback_integer_result =
80 typename std::enable_if<!is_safe_integer_operation<A, B>::value>::type;
81 } // namespace detail
82
83 /// A type safe integer class.
84 ///
85 /// This is a tiny, no overhead wrapper over a standard integer type.
86 /// It behaves exactly like the built-in types except that narrowing conversions are not allowed.
87 /// It also checks against `unsigned` under/overflow in debug mode
88 /// and marks it as undefined for the optimizer otherwise.
89 ///
90 /// A conversion is considered safe if both integer types have the same signedness
91 /// and the size of the value being converted is less than or equal to the destination size.
92 ///
93 /// \requires `IntegerT` must be an integral type except `bool` and `char` (use `signed
94 /// char`/`unsigned char`). \notes It intentionally does not provide the bitwise operations. \module
95 /// types
96 template <typename IntegerT, class Policy /* = arithmetic_policy_default*/>
97 class integer
98 {
99 static_assert(detail::is_integer<IntegerT>::value, "must be a real integer type");
100
101 public:
102 using integer_type = IntegerT;
103
104 //=== constructors ===//
105 #if TYPE_SAFE_DELETE_FUNCTIONS
106 /// \exclude
107 integer() = delete;
108 #endif
109
110 /// \effects Initializes it with the given value.
111 /// \notes This function does not participate in overload resolution
112 /// if `T` is not an integer type safely convertible to this type.
113 /// \group constructor
114 /// \param 1
115 /// \exclude
116 template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>>
integer(const T & val)117 TYPE_SAFE_FORCE_INLINE constexpr integer(const T& val) : value_(val)
118 {}
119
120 /// \group constructor
121 /// \param 1
122 /// \exclude
123 template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>>
integer(const integer<T,Policy> & val)124 TYPE_SAFE_FORCE_INLINE constexpr integer(const integer<T, Policy>& val)
125 : value_(static_cast<T>(val))
126 {}
127
128 #if TYPE_SAFE_DELETE_FUNCTIONS
129 /// \exclude
130 template <typename T, typename = detail::fallback_safe_integer_conversion<T, integer_type>>
131 constexpr integer(T) = delete;
132 /// \exclude
133 template <typename T, typename = detail::fallback_safe_integer_conversion<T, integer_type>>
134 constexpr integer(const integer<T, Policy>&) = delete;
135 #endif
136
137 //=== assignment ===//
138 /// \effects Assigns it with the given value.
139 /// \notes This function does not participate in overload resolution
140 /// if `T` is not an integer type safely convertible to this type.
141 /// \group assignment
142 /// \param 1
143 /// \exclude
144 template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>>
operator =(const T & val)145 TYPE_SAFE_FORCE_INLINE integer& operator=(const T& val)
146 {
147 value_ = val;
148 return *this;
149 }
150
151 /// \group assignment
152 /// \param 1
153 /// \exclude
154 template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>>
operator =(const integer<T,Policy> & val)155 TYPE_SAFE_FORCE_INLINE integer& operator=(const integer<T, Policy>& val)
156 {
157 value_ = static_cast<T>(val);
158 return *this;
159 }
160
161 #if TYPE_SAFE_DELETE_FUNCTIONS
162 /// \exclude
163 template <typename T, typename = detail::fallback_safe_integer_conversion<T, integer_type>>
164 constexpr integer(T) = delete;
165 /// \exclude
166 template <typename T, typename = detail::fallback_safe_integer_conversion<T, integer_type>>
167 integer& operator=(const integer<T, Policy>&) = delete;
168 #endif
169
170 //=== conversion back ===//
171 /// \returns The stored value as the native integer type.
172 /// \group conversion
operator integer_type() const173 TYPE_SAFE_FORCE_INLINE explicit constexpr operator integer_type() const noexcept
174 {
175 return value_;
176 }
177
178 /// \group conversion
get() const179 TYPE_SAFE_FORCE_INLINE constexpr integer_type get() const noexcept
180 {
181 return value_;
182 }
183
184 //=== unary operators ===//
185 /// \returns The integer type unchanged.
operator +() const186 TYPE_SAFE_FORCE_INLINE constexpr integer operator+() const
187 {
188 return *this;
189 }
190
191 /// \returns The negative integer type.
192 /// \requires The integer type must not be unsigned.
operator -() const193 TYPE_SAFE_FORCE_INLINE constexpr integer operator-() const
194 {
195 static_assert(std::is_signed<integer_type>::value,
196 "cannot call unary minus on unsigned integer");
197 return integer(Policy::template do_multiplication(value_, integer_type(-1)));
198 }
199
200 /// \effects Increments the integer by one.
201 /// \group increment
operator ++()202 TYPE_SAFE_FORCE_INLINE integer& operator++()
203 {
204 value_ = Policy::template do_addition(value_, integer_type(1));
205 return *this;
206 }
207
208 /// \group increment
operator ++(int)209 TYPE_SAFE_FORCE_INLINE integer operator++(int)
210 {
211 auto res = *this;
212 ++*this;
213 return res;
214 }
215
216 /// \effects Decrements the integer by one.
217 /// \group decrement
operator --()218 TYPE_SAFE_FORCE_INLINE integer& operator--()
219 {
220 value_ = Policy::template do_subtraction(value_, integer_type(1));
221 return *this;
222 }
223
224 /// \group decrement
operator --(int)225 TYPE_SAFE_FORCE_INLINE integer operator--(int)
226 {
227 auto res = *this;
228 --*this;
229 return res;
230 }
231
232 //=== compound assignment ====//
233 /// \exclude
234 #define TYPE_SAFE_DETAIL_MAKE_OP(Op) \
235 /** \group compound_assign \
236 * \param 1 \
237 * \exclude */ \
238 template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>> \
239 TYPE_SAFE_FORCE_INLINE integer& operator Op(const T& other) \
240 { \
241 return *this Op integer<T, Policy>(other); \
242 } \
243 /** \exclude */ \
244 template <typename T, typename = detail::fallback_safe_integer_conversion<T, integer_type>> \
245 integer& operator Op(integer<T, Policy>) = delete; \
246 /** \exclude */ \
247 template <typename T, typename = detail::fallback_safe_integer_conversion<T, integer_type>> \
248 integer& operator Op(T) = delete;
249
250 /// \effects Same as the operation on the integer type.
251 /// \notes These functions do not participate in overload resolution,
252 /// if `T` is not an integer type safely convertible to this type.
253 /// \group compound_assign Compound assignment
254 /// \param 1
255 /// \exclude
256 template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>>
operator +=(const integer<T,Policy> & other)257 TYPE_SAFE_FORCE_INLINE integer& operator+=(const integer<T, Policy>& other)
258 {
259 value_ = Policy::template do_addition<integer_type>(value_, static_cast<T>(other));
260 return *this;
261 }
262 TYPE_SAFE_DETAIL_MAKE_OP(+=)
263
264 /// \group compound_assign
265 /// \param 1
266 /// \exclude
267 template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>>
operator -=(const integer<T,Policy> & other)268 TYPE_SAFE_FORCE_INLINE integer& operator-=(const integer<T, Policy>& other)
269 {
270 value_ = Policy::template do_subtraction<integer_type>(value_, static_cast<T>(other));
271 return *this;
272 return *this;
273 }
274 TYPE_SAFE_DETAIL_MAKE_OP(-=)
275
276 /// \group compound_assign
277 /// \param 1
278 /// \exclude
279 template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>>
operator *=(const integer<T,Policy> & other)280 TYPE_SAFE_FORCE_INLINE integer& operator*=(const integer<T, Policy>& other)
281 {
282 value_ = Policy::template do_multiplication<integer_type>(value_, static_cast<T>(other));
283 return *this;
284 }
285 TYPE_SAFE_DETAIL_MAKE_OP(*=)
286
287 /// \group compound_assign
288 /// \param 1
289 /// \exclude
290 template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>>
operator /=(const integer<T,Policy> & other)291 TYPE_SAFE_FORCE_INLINE integer& operator/=(const integer<T, Policy>& other)
292 {
293 value_ = Policy::template do_division<integer_type>(value_, static_cast<T>(other));
294 return *this;
295 }
296 TYPE_SAFE_DETAIL_MAKE_OP(/=)
297
298 /// \group compound_assign
299 /// \param 1
300 /// \exclude
301 template <typename T, typename = detail::enable_safe_integer_conversion<T, integer_type>>
operator %=(const integer<T,Policy> & other)302 TYPE_SAFE_FORCE_INLINE integer& operator%=(const integer<T, Policy>& other)
303 {
304 value_ = Policy::template do_modulo<integer_type>(value_, static_cast<T>(other));
305 return *this;
306 }
307 TYPE_SAFE_DETAIL_MAKE_OP(%=)
308
309 #undef TYPE_SAFE_DETAIL_MAKE_OP
310
311 private:
312 integer_type value_;
313 };
314
315 //=== operations ===//
316 /// \exclude
317 namespace detail
318 {
319 template <typename T>
320 struct make_signed
321 {
322 using type = typename std::make_signed<T>::type;
323 };
324
325 template <typename T, class Policy>
326 struct make_signed<integer<T, Policy>>
327 {
328 using type = integer<typename std::make_signed<T>::type, Policy>;
329 };
330
331 template <typename T>
332 struct make_unsigned
333 {
334 using type = typename std::make_unsigned<T>::type;
335 };
336
337 template <typename T, class Policy>
338 struct make_unsigned<integer<T, Policy>>
339 {
340 using type = integer<typename std::make_unsigned<T>::type, Policy>;
341 };
342 } // namespace detail
343
344 /// [std::make_signed]() for [ts::integer]().
345 /// \module types
346 /// \exclude target
347 template <class Integer>
348 using make_signed_t = typename detail::make_signed<Integer>::type;
349
350 /// \returns A new integer of the corresponding signed integer type.
351 /// \requires The value of `i` must fit into signed type.
352 /// \module types
353 /// \param 1
354 /// \exclude
355 template <typename Integer,
356 typename = typename std::enable_if<detail::is_integer<Integer>::value>::type>
make_signed(const Integer & i)357 TYPE_SAFE_FORCE_INLINE constexpr make_signed_t<Integer> make_signed(const Integer& i)
358 {
359 using result_type = make_signed_t<Integer>;
360 return i <= Integer(std::numeric_limits<result_type>::max())
361 ? static_cast<result_type>(i)
362 : DEBUG_UNREACHABLE(detail::precondition_error_handler{}, "conversion "
363 "would "
364 "overflow");
365 }
366
367 /// \returns A new [ts::integer]() of the corresponding signed integer type.
368 /// \requires The value of `i` must fit into signed type.
369 /// \module types
370 template <typename Integer, class Policy>
make_signed(const integer<Integer,Policy> & i)371 TYPE_SAFE_FORCE_INLINE constexpr make_signed_t<integer<Integer, Policy>> make_signed(
372 const integer<Integer, Policy>& i)
373 {
374 return make_signed(static_cast<Integer>(i));
375 }
376
377 /// [std::make_unsigned]() for [ts::integer]().
378 /// \module types
379 /// \exclude target
380 template <class Integer>
381 using make_unsigned_t = typename detail::make_unsigned<Integer>::type;
382
383 /// \returns A new integer of the corresponding unsigned integer type.
384 /// \requires The value of `i` must not be negative.
385 /// \module types
386 /// \param 1
387 /// \exclude
388 template <typename Integer,
389 typename = typename std::enable_if<detail::is_integer<Integer>::value>::type>
make_unsigned(const Integer & i)390 TYPE_SAFE_FORCE_INLINE constexpr make_unsigned_t<Integer> make_unsigned(const Integer& i)
391 {
392 using result_type = make_unsigned_t<Integer>;
393 return i >= Integer(0) ? static_cast<result_type>(i)
394 : DEBUG_UNREACHABLE(detail::precondition_error_handler{},
395 "conversion would underflow");
396 }
397
398 /// \returns A new [ts::integer]() of the corresponding unsigned integer type.
399 /// \requires The value of `i` must not be negative.
400 /// \module types
401 template <typename Integer, class Policy>
make_unsigned(const integer<Integer,Policy> & i)402 TYPE_SAFE_FORCE_INLINE constexpr make_unsigned_t<integer<Integer, Policy>> make_unsigned(
403 const integer<Integer, Policy>& i)
404 {
405 return make_unsigned(static_cast<Integer>(i));
406 }
407
408 /// \returns The absolute value of a built-in signed integer.
409 /// It will be changed to the unsigned return type as well.
410 /// \module types
411 /// \param 1
412 /// \exclude
413 template <typename SignedInteger,
414 typename = typename std::enable_if<std::is_signed<SignedInteger>::value>::type>
abs(const SignedInteger & i)415 TYPE_SAFE_FORCE_INLINE constexpr make_unsigned_t<SignedInteger> abs(const SignedInteger& i)
416 {
417 return make_unsigned(i > 0 ? i : -i);
418 }
419
420 /// \returns The absolute value of an [ts::integer]().
421 /// \module types
422 /// \param 2
423 /// \exclude
424 template <typename SignedInteger, class Policy,
425 typename = typename std::enable_if<std::is_signed<SignedInteger>::value>::type>
abs(const integer<SignedInteger,Policy> & i)426 TYPE_SAFE_FORCE_INLINE constexpr make_unsigned_t<integer<SignedInteger, Policy>> abs(
427 const integer<SignedInteger, Policy>& i)
428 {
429 return make_unsigned(i > 0 ? i : -i);
430 }
431
432 /// \returns `i` unchanged.
433 /// \notes This is an optimization of `abs()` for unsigned integer types.
434 /// \module types
435 /// \param 1
436 /// \exclude
437 template <typename UnsignedInteger,
438 typename = typename std::enable_if<std::is_unsigned<UnsignedInteger>::value>::type>
abs(const UnsignedInteger & i)439 TYPE_SAFE_FORCE_INLINE constexpr UnsignedInteger abs(const UnsignedInteger& i)
440 {
441 return i;
442 }
443
444 /// \returns `i` unchanged.
445 /// \notes This is an optimization of `abs()` for unsigned integer types.
446 /// \module types
447 /// \param 2
448 /// \exclude
449 template <typename UnsignedInteger, class Policy,
450 typename = typename std::enable_if<std::is_unsigned<UnsignedInteger>::value>::type>
abs(const integer<UnsignedInteger,Policy> & i)451 TYPE_SAFE_FORCE_INLINE constexpr integer<UnsignedInteger, Policy> abs(
452 const integer<UnsignedInteger, Policy>& i)
453 {
454 return i;
455 }
456
457 //=== comparison ===//
458 /// \exclude
459 #define TYPE_SAFE_DETAIL_MAKE_OP(Op) \
460 /** \group int_comp \
461 * \param 3 \
462 * \exclude */ \
463 template <typename A, typename B, class Policy, \
464 typename = detail::enable_safe_integer_comparison<A, B>> \
465 TYPE_SAFE_FORCE_INLINE constexpr bool operator Op(const A& a, const integer<B, Policy>& b) \
466 { \
467 return integer<A, Policy>(a) Op b; \
468 } \
469 /** \group int_comp \
470 * \param 3 \
471 * \exclude */ \
472 template <typename A, class Policy, typename B, \
473 typename = detail::enable_safe_integer_comparison<A, B>> \
474 TYPE_SAFE_FORCE_INLINE constexpr bool operator Op(const integer<A, Policy>& a, const B& b) \
475 { \
476 return a Op integer<B, Policy>(b); \
477 } \
478 /** \exclude */ \
479 template <typename A, class Policy, typename B, \
480 typename = detail::fallback_safe_integer_comparison<A, B>> \
481 constexpr bool operator Op(integer<A, Policy>, integer<B, Policy>) = delete; \
482 /** \exclude */ \
483 template <typename A, typename B, class Policy, \
484 typename = detail::fallback_safe_integer_comparison<A, B>> \
485 constexpr bool operator Op(A, integer<B, Policy>) = delete; \
486 /** \exclude */ \
487 template <typename A, class Policy, typename B, \
488 typename = detail::fallback_safe_integer_comparison<A, B>> \
489 constexpr bool operator Op(integer<A, Policy>, B) = delete;
490
491 /// \returns The result of the comparison of the stored integer value in the [ts::integer]().
492 /// \notes These functions do not participate in overload resolution
493 /// unless `A` and `B` are both integer types.
494 /// \group int_comp Comparison operators
495 /// \module types
496 /// \param 3
497 /// \exclude
498 template <typename A, typename B, class Policy,
499 typename = detail::enable_safe_integer_comparison<A, B>>
operator ==(const integer<A,Policy> & a,const integer<B,Policy> & b)500 TYPE_SAFE_FORCE_INLINE constexpr bool operator==(const integer<A, Policy>& a,
501 const integer<B, Policy>& b)
502 {
503 return static_cast<A>(a) == static_cast<B>(b);
504 }
505 TYPE_SAFE_DETAIL_MAKE_OP(==)
506
507 /// \group int_comp Comparison operators
508 /// \param 3
509 /// \exclude
510 template <typename A, typename B, class Policy,
511 typename = detail::enable_safe_integer_comparison<A, B>>
operator !=(const integer<A,Policy> & a,const integer<B,Policy> & b)512 TYPE_SAFE_FORCE_INLINE constexpr bool operator!=(const integer<A, Policy>& a,
513 const integer<B, Policy>& b)
514 {
515 return static_cast<A>(a) != static_cast<B>(b);
516 }
517 TYPE_SAFE_DETAIL_MAKE_OP(!=)
518
519 /// \group int_comp Comparison operators
520 /// \param 3
521 /// \exclude
522 template <typename A, typename B, class Policy,
523 typename = detail::enable_safe_integer_comparison<A, B>>
operator <(const integer<A,Policy> & a,const integer<B,Policy> & b)524 TYPE_SAFE_FORCE_INLINE constexpr bool operator<(const integer<A, Policy>& a,
525 const integer<B, Policy>& b)
526 {
527 return static_cast<A>(a) < static_cast<B>(b);
528 }
529 TYPE_SAFE_DETAIL_MAKE_OP(<)
530
531 /// \group int_comp Comparison operators
532 /// \param 3
533 /// \exclude
534 template <typename A, typename B, class Policy,
535 typename = detail::enable_safe_integer_comparison<A, B>>
operator <=(const integer<A,Policy> & a,const integer<B,Policy> & b)536 TYPE_SAFE_FORCE_INLINE constexpr bool operator<=(const integer<A, Policy>& a,
537 const integer<B, Policy>& b)
538 {
539 return static_cast<A>(a) <= static_cast<B>(b);
540 }
541 TYPE_SAFE_DETAIL_MAKE_OP(<=)
542
543 /// \group int_comp Comparison operators
544 /// \param 3
545 /// \exclude
546 template <typename A, typename B, class Policy,
547 typename = detail::enable_safe_integer_comparison<A, B>>
operator >(const integer<A,Policy> & a,const integer<B,Policy> & b)548 TYPE_SAFE_FORCE_INLINE constexpr bool operator>(const integer<A, Policy>& a,
549 const integer<B, Policy>& b)
550 {
551 return static_cast<A>(a) > static_cast<B>(b);
552 }
553 TYPE_SAFE_DETAIL_MAKE_OP(>)
554
555 /// \group int_comp Comparison operators
556 /// \param 3
557 /// \exclude
558 template <typename A, typename B, class Policy,
559 typename = detail::enable_safe_integer_comparison<A, B>>
operator >=(const integer<A,Policy> & a,const integer<B,Policy> & b)560 TYPE_SAFE_FORCE_INLINE constexpr bool operator>=(const integer<A, Policy>& a,
561 const integer<B, Policy>& b)
562 {
563 return static_cast<A>(a) >= static_cast<B>(b);
564 }
565 TYPE_SAFE_DETAIL_MAKE_OP(>=)
566
567 #undef TYPE_SAFE_DETAIL_MAKE_OP
568
569 //=== binary operations ===//
570 /// \entity TYPE_SAFE_DETAIL_MAKE_OP
571 /// \exclude
572 #define TYPE_SAFE_DETAIL_MAKE_OP(Op) \
573 /** \exclude return \
574 * \group int_binary_op */ \
575 template <typename A, typename B, class Policy> \
576 TYPE_SAFE_FORCE_INLINE constexpr auto operator Op(const A& a, const integer<B, Policy>& b) \
577 ->integer<detail::integer_result_t<A, B>, Policy> \
578 { \
579 return integer<A, Policy>(a) Op b; \
580 } \
581 /** \exclude return \
582 * \group int_binary_op */ \
583 template <typename A, class Policy, typename B> \
584 TYPE_SAFE_FORCE_INLINE constexpr auto operator Op(const integer<A, Policy>& a, const B& b) \
585 ->integer<detail::integer_result_t<A, B>, Policy> \
586 { \
587 return a Op integer<B, Policy>(b); \
588 } \
589 /** \exclude */ \
590 template <typename A, typename B, class Policy, \
591 typename = detail::fallback_integer_result<A, B>> \
592 constexpr int operator Op(integer<A, Policy>, integer<B, Policy>) = delete; \
593 /** \exclude */ \
594 template <typename A, typename B, class Policy, \
595 typename = detail::fallback_integer_result<A, B>> \
596 constexpr int operator Op(A, integer<B, Policy>) = delete; \
597 /** \exclude */ \
598 template <typename A, class Policy, typename B, \
599 typename = detail::fallback_integer_result<A, B>> \
600 constexpr int operator Op(integer<A, Policy>, B) = delete;
601
602 /// \returns The result of the binary operation of the stored integer value in the [ts::integer]().
603 /// The type is a [ts::integer]() of the bigger integer type.
604 /// \notes These functions do not participate in overload resolution,
605 /// unless `A` and `B` are both integer types.
606 /// \group int_binary_op Binary operations
607 /// \module types
608 /// \exclude return
609 template <typename A, typename B, class Policy>
operator +(const integer<A,Policy> & a,const integer<B,Policy> & b)610 TYPE_SAFE_FORCE_INLINE constexpr auto operator+(const integer<A, Policy>& a,
611 const integer<B, Policy>& b)
612 -> integer<detail::integer_result_t<A, B>, Policy>
613 {
614 using type = detail::integer_result_t<A, B>;
615 return Policy::template do_addition<type>(static_cast<A>(a), static_cast<B>(b));
616 }
617 TYPE_SAFE_DETAIL_MAKE_OP(+)
618
619 /// \group int_binary_op
620 /// \exclude return
621 template <typename A, typename B, class Policy>
operator -(const integer<A,Policy> & a,const integer<B,Policy> & b)622 TYPE_SAFE_FORCE_INLINE constexpr auto operator-(const integer<A, Policy>& a,
623 const integer<B, Policy>& b)
624 -> integer<detail::integer_result_t<A, B>, Policy>
625 {
626 using type = detail::integer_result_t<A, B>;
627 return Policy::template do_subtraction<type>(static_cast<A>(a), static_cast<B>(b));
628 }
629 TYPE_SAFE_DETAIL_MAKE_OP(-)
630
631 /// \group int_binary_op
632 /// \exclude return
633 template <typename A, typename B, class Policy>
operator *(const integer<A,Policy> & a,const integer<B,Policy> & b)634 TYPE_SAFE_FORCE_INLINE constexpr auto operator*(const integer<A, Policy>& a,
635 const integer<B, Policy>& b)
636 -> integer<detail::integer_result_t<A, B>, Policy>
637 {
638 using type = detail::integer_result_t<A, B>;
639 return Policy::template do_multiplication<type>(static_cast<A>(a), static_cast<B>(b));
640 }
641 TYPE_SAFE_DETAIL_MAKE_OP(*)
642
643 /// \group int_binary_op
644 /// \exclude return
645 template <typename A, typename B, class Policy>
operator /(const integer<A,Policy> & a,const integer<B,Policy> & b)646 TYPE_SAFE_FORCE_INLINE constexpr auto operator/(const integer<A, Policy>& a,
647 const integer<B, Policy>& b)
648 -> integer<detail::integer_result_t<A, B>, Policy>
649 {
650 using type = detail::integer_result_t<A, B>;
651 return Policy::template do_division<type>(static_cast<A>(a), static_cast<B>(b));
652 }
653 TYPE_SAFE_DETAIL_MAKE_OP(/)
654
655 /// \group int_binary_op
656 /// \exclude return
657 template <typename A, typename B, class Policy>
operator %(const integer<A,Policy> & a,const integer<B,Policy> & b)658 TYPE_SAFE_FORCE_INLINE constexpr auto operator%(const integer<A, Policy>& a,
659 const integer<B, Policy>& b)
660 -> integer<detail::integer_result_t<A, B>, Policy>
661 {
662 using type = detail::integer_result_t<A, B>;
663 return Policy::template do_modulo<type>(static_cast<A>(a), static_cast<B>(b));
664 }
665 TYPE_SAFE_DETAIL_MAKE_OP(%)
666
667 #undef TYPE_SAFE_DETAIL_MAKE_OP
668
669 //=== input/output ===/
670 /// \effects Reads an integer from the [std::istream]() and assigns it to the given [ts::integer]().
671 /// \module types
672 /// \output_section Input/output
673 template <typename Char, class CharTraits, typename IntegerT, class Policy>
operator >>(std::basic_istream<Char,CharTraits> & in,integer<IntegerT,Policy> & i)674 std::basic_istream<Char, CharTraits>& operator>>(std::basic_istream<Char, CharTraits>& in,
675 integer<IntegerT, Policy>& i)
676 {
677 IntegerT val;
678 in >> val;
679 i = val;
680 return in;
681 }
682
683 /// \effects Converts the given [ts::integer]() to the underlying integer type and writes it to th
684 /// [std::ostream](). \module types
685 template <typename Char, class CharTraits, typename IntegerT, class Policy>
operator <<(std::basic_ostream<Char,CharTraits> & out,const integer<IntegerT,Policy> & i)686 std::basic_ostream<Char, CharTraits>& operator<<(std::basic_ostream<Char, CharTraits>& out,
687 const integer<IntegerT, Policy>& i)
688 {
689 return out << static_cast<IntegerT>(i);
690 }
691 } // namespace type_safe
692
693 namespace std
694 {
695 /// Hash specialization for [ts::integer].
696 /// \module types
697 template <typename IntegerT, class Policy>
698 struct hash<type_safe::integer<IntegerT, Policy>>
699 {
operator ()std::hash700 std::size_t operator()(const type_safe::integer<IntegerT, Policy>& i) const noexcept
701 {
702 return std::hash<IntegerT>()(static_cast<IntegerT>(i));
703 }
704 };
705 } // namespace std
706
707 #endif // TYPE_SAFE_INTEGER_HPP_INCLUDED
708