|
29 | 29 |
|
30 | 30 | #include <limits> |
31 | 31 | #include <stdexcept> |
| 32 | +#include <type_traits> |
32 | 33 |
|
33 | 34 | #ifdef _MSC_VER |
34 | 35 | #include <Intsafe.h> |
@@ -331,4 +332,103 @@ namespace Safe |
331 | 332 | return num < 0 ? -num : num; |
332 | 333 | } |
333 | 334 |
|
| 335 | + namespace Internal |
| 336 | + { |
| 337 | + template <typename from, typename to, typename = void> |
| 338 | + struct is_safely_convertible : std::false_type |
| 339 | + { |
| 340 | + static_assert(std::is_integral<from>::value&& std::is_integral<to>::value, |
| 341 | + "from and to must both be integral types"); |
| 342 | + }; |
| 343 | + |
| 344 | + template <typename from, typename to> |
| 345 | + struct is_safely_convertible< |
| 346 | + from, to, |
| 347 | + typename std::enable_if<((std::numeric_limits<from>::max() <= std::numeric_limits<to>::max()) && |
| 348 | + (std::numeric_limits<from>::min() >= std::numeric_limits<to>::min()))>::type> |
| 349 | + : std::true_type |
| 350 | + { |
| 351 | + }; |
| 352 | + |
| 353 | + template <typename T, typename U, typename = void> |
| 354 | + struct have_same_signedness : std::false_type |
| 355 | + { |
| 356 | + static_assert(std::is_integral<T>::value&& std::is_integral<U>::value, |
| 357 | + "T and U must both be integral types"); |
| 358 | + }; |
| 359 | + |
| 360 | + // SFINAE overload for (T signed and U signed) or (T unsigned and U unsigned) |
| 361 | + // note: !a != !b == a XOR b (for a, b bool) |
| 362 | + template <typename T, typename U> |
| 363 | + struct have_same_signedness< |
| 364 | + T, U, typename std::enable_if<!((!std::is_signed<T>::value) != (!std::is_signed<U>::value))>::type> |
| 365 | + : std::true_type |
| 366 | + { |
| 367 | + }; |
| 368 | + |
| 369 | + } // namespace Internal |
| 370 | + |
| 371 | +#ifdef PARSED_BY_DOXYGEN |
| 372 | + /// Convert a value of type U to type T without causing over- or underflows. |
| 373 | + /// |
| 374 | + /// @throw std::overflow_error When `value` is outside the representable range of T |
| 375 | + template <typename T, typename U> |
| 376 | + constexpr T cast(U value) |
| 377 | + { |
| 378 | + } |
| 379 | +#else |
| 380 | + // trivial version: T can represent all values that U can |
| 381 | + template <typename T, typename U> |
| 382 | + constexpr typename std::enable_if<Internal::is_safely_convertible<U, T>::value, T>::type cast(U value) noexcept |
| 383 | + { |
| 384 | + return static_cast<T>(value); |
| 385 | + } |
| 386 | + |
| 387 | + // T cannot represent all values that U can, |
| 388 | + // but T and U are either both signed or unsigned |
| 389 | + // => can compare them without any issues |
| 390 | + template <typename T, typename U> |
| 391 | + constexpr typename std::enable_if< |
| 392 | + (!Internal::is_safely_convertible<U, T>::value) && Internal::have_same_signedness<T, U>::value, T>::type |
| 393 | + cast(U value) |
| 394 | + { |
| 395 | + return (value <= std::numeric_limits<T>::max()) && (value >= std::numeric_limits<T>::min()) |
| 396 | + ? static_cast<T>(value) |
| 397 | + : throw std::overflow_error("Cannot convert number without over or underflow"); |
| 398 | + } |
| 399 | + |
| 400 | + // - T cannot represent all values that U can, |
| 401 | + // - T is signed, U is unsigned |
| 402 | + // => must cast them compare them without any issues |
| 403 | + template <typename T, typename U> |
| 404 | + constexpr typename std::enable_if<(!Internal::is_safely_convertible<U, T>::value) && std::is_signed<T>::value && |
| 405 | + std::is_unsigned<U>::value, |
| 406 | + T>::type |
| 407 | + cast(U value) |
| 408 | + { |
| 409 | + static_assert(std::numeric_limits<T>::max() < std::numeric_limits<U>::max(), |
| 410 | + "maximum value of T must be smaller than the maximum value of U"); |
| 411 | + // U unsigned, T signed => T_MAX < U_MAX |
| 412 | + return (value <= static_cast<U>(std::numeric_limits<T>::max())) && (value >= 0) |
| 413 | + ? static_cast<T>(value) |
| 414 | + : throw std::overflow_error("Cannot convert number without over or underflow"); |
| 415 | + } |
| 416 | + |
| 417 | + // - T cannot represent all values that U can, |
| 418 | + // - T is unsigned, U is signed |
| 419 | + // => must cast them compare them without any issues |
| 420 | + template <typename T, typename U> |
| 421 | + constexpr typename std::enable_if<(!Internal::is_safely_convertible<U, T>::value) && std::is_unsigned<T>::value && |
| 422 | + std::is_signed<U>::value, |
| 423 | + T>::type |
| 424 | + cast(U value) |
| 425 | + { |
| 426 | + // U signed, T unsigned => T_MAX < U_MAX |
| 427 | + return (value <= std::numeric_limits<T>::max()) && (value >= std::numeric_limits<T>::min()) |
| 428 | + ? static_cast<T>(value) |
| 429 | + : throw std::overflow_error("Cannot convert number without over or underflow"); |
| 430 | + } |
| 431 | + |
| 432 | +#endif // PARSED_BY_DOXYGEN |
| 433 | + |
334 | 434 | } // namespace Safe |
0 commit comments