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