Last active
August 22, 2021 18:25
-
-
Save 0x1F9F1/5ca82e95e3d91b82c95672c9c11de149 to your computer and use it in GitHub Desktop.
Safely cast integers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <limits> | |
#include <type_traits> | |
enum class int_cast_mode | |
{ | |
lossless, | |
check_sign, | |
check_max, | |
check_trip, | |
}; | |
template <typename T, bool Lossless> | |
struct int_cast_result; | |
template <typename T> | |
struct int_cast_result<T, true> | |
{ | |
T value; | |
static constexpr bool success = true; | |
constexpr inline T operator*() const noexcept | |
{ | |
return value; | |
} | |
constexpr inline explicit operator bool() const noexcept | |
{ | |
return true; | |
} | |
}; | |
template <typename T> | |
struct int_cast_result<T, false> | |
{ | |
T value; | |
bool success; | |
constexpr inline T operator*() const noexcept | |
{ | |
return value; | |
} | |
constexpr inline explicit operator bool() const noexcept | |
{ | |
return success; | |
} | |
}; | |
template <typename From, typename To> | |
struct int_caster | |
{ | |
using from_limits = std::numeric_limits<From>; | |
using to_limits = std::numeric_limits<To>; | |
static_assert(from_limits::is_integer && to_limits::is_integer, "Invalid Types"); | |
static constexpr int_cast_mode mode = (from_limits::digits > to_limits::digits) | |
? from_limits::is_signed | |
? int_cast_mode::check_trip | |
: int_cast_mode::check_max | |
: (from_limits::is_signed && !to_limits::is_signed) | |
? int_cast_mode::check_sign | |
: int_cast_mode::lossless; | |
using result = int_cast_result<To, mode == int_cast_mode::lossless>; | |
}; | |
template <typename From, typename To, int_cast_mode Mode, typename Caster = int_caster<From, To>> | |
using int_caster_result_if = typename std::enable_if<Mode == Caster::mode, typename Caster::result>::type; | |
template <typename To, typename From> | |
constexpr inline int_caster_result_if<From, To, int_cast_mode::lossless> int_cast(From value) noexcept | |
{ | |
return {static_cast<To>(value)}; | |
} | |
template <typename To, typename From> | |
constexpr inline int_caster_result_if<From, To, int_cast_mode::check_sign> int_cast(From value) noexcept | |
{ | |
return {static_cast<To>(value), value >= static_cast<From>(0)}; | |
} | |
template <typename To, typename From> | |
constexpr inline int_caster_result_if<From, To, int_cast_mode::check_max> int_cast(From value) noexcept | |
{ | |
return {static_cast<To>(value), value <= static_cast<From>((std::numeric_limits<To>::max)())}; | |
} | |
template <typename To, typename From> | |
constexpr inline int_caster_result_if<From, To, int_cast_mode::check_trip> int_cast(From value) noexcept | |
{ | |
return {static_cast<To>(value), value == static_cast<From>(static_cast<To>(value))}; | |
} | |
#include <climits> | |
int main() | |
{ | |
// signed -> signed | |
// Truncate with matching signs, checks for round trip | |
static_assert(!int_cast<short, int>(INT_MIN), ""); | |
static_assert(!int_cast<short, int>(SHRT_MIN - 1), ""); | |
static_assert(int_cast<short, int>(SHRT_MIN), ""); | |
static_assert(int_cast<short, int>(0), ""); | |
static_assert(int_cast<short, int>(SHRT_MAX), ""); | |
static_assert(!int_cast<short, int>(SHRT_MAX + 1), ""); | |
static_assert(!int_cast<short, int>(INT_MAX), ""); | |
// Extending with matching signs should always work | |
static_assert(int_cast<int, short>(SHRT_MAX), ""); | |
static_assert(int_cast<int, short>(0), ""); | |
static_assert(int_cast<int, short>(SHRT_MIN), ""); | |
static_assert(int_cast<short, short>(SHRT_MAX), ""); | |
static_assert(int_cast<short, short>(SHRT_MIN), ""); | |
// unsigned -> unsigned | |
static_assert(int_cast<unsigned int, unsigned short>(0), ""); | |
static_assert(int_cast<unsigned int, unsigned short>(USHRT_MAX), ""); | |
static_assert(int_cast<unsigned short, unsigned short>(0), ""); | |
static_assert(int_cast<unsigned short, unsigned short>(USHRT_MAX), ""); | |
static_assert(int_cast<unsigned short, unsigned int>(0), ""); | |
static_assert(int_cast<unsigned short, unsigned int>(USHRT_MAX), ""); | |
static_assert(!int_cast<unsigned short, unsigned int>(USHRT_MAX + 1), ""); | |
static_assert(!int_cast<unsigned short, unsigned int>(UINT_MAX), ""); | |
// signed -> unsigned | |
static_assert(!int_cast<unsigned short, int>(INT_MIN), ""); | |
static_assert(!int_cast<unsigned short, int>(SHRT_MIN - 1), ""); | |
static_assert(!int_cast<unsigned short, int>(SHRT_MIN), ""); | |
static_assert(!int_cast<unsigned short, int>(-1), ""); | |
static_assert(int_cast<unsigned short, int>(0), ""); | |
static_assert(int_cast<unsigned short, int>(USHRT_MAX - 1), ""); | |
static_assert(int_cast<unsigned short, int>(USHRT_MAX), ""); | |
static_assert(!int_cast<unsigned short, int>(USHRT_MAX + 1), ""); | |
static_assert(!int_cast<unsigned short, int>(INT_MAX), ""); | |
static_assert(!int_cast<unsigned short, short>(SHRT_MIN), ""); | |
static_assert(!int_cast<unsigned short, short>(-1), ""); | |
static_assert(int_cast<unsigned short, short>(0), ""); | |
static_assert(int_cast<unsigned short, short>(SHRT_MAX - 1), ""); | |
static_assert(int_cast<unsigned short, short>(SHRT_MAX), ""); | |
// unsigned -> signed | |
static_assert(int_cast<short, unsigned char>(0), ""); | |
static_assert(int_cast<short, unsigned char>(UCHAR_MAX), ""); | |
static_assert(int_cast<short, unsigned short>(0), ""); | |
static_assert(int_cast<short, unsigned short>(SHRT_MAX), ""); | |
static_assert(!int_cast<short, unsigned short>(SHRT_MAX + 1), ""); | |
static_assert(!int_cast<short, unsigned short>(USHRT_MAX), ""); | |
static_assert(int_cast<short, unsigned int>(0), ""); | |
static_assert(int_cast<short, unsigned int>(SHRT_MAX), ""); | |
static_assert(!int_cast<short, unsigned int>(SHRT_MAX + 1), ""); | |
static_assert(!int_cast<short, unsigned int>(UINT_MAX), ""); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment