Skip to content

Instantly share code, notes, and snippets.

@0x1F9F1
Last active August 22, 2021 18:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0x1F9F1/5ca82e95e3d91b82c95672c9c11de149 to your computer and use it in GitHub Desktop.
Save 0x1F9F1/5ca82e95e3d91b82c95672c9c11de149 to your computer and use it in GitHub Desktop.
Safely cast integers
#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