Skip to content
Snippets Groups Projects
Commit eca2866f authored by Tristan Walter's avatar Tristan Walter
Browse files

updating narrow_cast

parent 9010b155
No related branches found
No related tags found
No related merge requests found
...@@ -1040,91 +1040,116 @@ struct warn_on_error {}; ...@@ -1040,91 +1040,116 @@ struct warn_on_error {};
struct fail_on_error {}; struct fail_on_error {};
} }
template<typename T>
using try_make_unsigned =
typename std::conditional<
std::is_integral<T>::value,
std::make_unsigned<T>,
double
>::type;
template<typename T>
using try_make_signed =
typename std::conditional<
std::is_integral<T>::value,
std::make_signed<T>,
double
>::type;
template<typename To, typename From> template<typename To, typename From>
constexpr To narrow_cast(From&& value, struct tag::warn_on_error) { constexpr bool check_narrow_cast(const From& value) {
using FromType = typename remove_cvref<From>::type; using FromType = typename remove_cvref<From>::type;
using ToType = typename remove_cvref<To>::type; using ToType = typename remove_cvref<To>::type;
if constexpr (sizeof(FromType) <= sizeof(ToType)) auto str = Meta::toStr(value);
return static_cast<ToType>(std::forward<From>(value)); if constexpr (
std::is_floating_point<ToType>::value
static_assert(sizeof(FromType) > sizeof(ToType) || (std::is_unsigned<FromType>::value && std::is_signed<ToType>::value), "From type has to be bigger than to type."); || (std::is_signed<FromType>::value == std::is_signed<ToType>::value && !std::is_floating_point<FromType>::value)
//static_assert(std::numeric_limits<ToType>::max() <= std::numeric_limits<FromType>::max(), "Maximum numeric limits of To type are larger than From type."); )
//static_assert(std::numeric_limits<ToType>::min() >= std::numeric_limits<FromType>::min(), "Minimum numeric limits of To type are smaller than From type."); {
bool out_ranged = value > (FromType)std::numeric_limits<To>::max()
|| value < (FromType)std::numeric_limits<To>::min();
if constexpr(
std::is_floating_point<FromType>::value
|| (std::is_unsigned<FromType>::value && std::is_unsigned<ToType>::value)
|| (std::is_signed<FromType>::value && std::is_signed<ToType>::value)
) {
// unsigned to unsigned // unsigned to unsigned
#ifdef _NARROW_PRINT_VERBOSE
} else if constexpr(std::is_unsigned<FromType>::value && std::is_signed<ToType>::value) { auto tstr0 = Meta::name<FromType>();
auto tstr1 = Meta::name<ToType>();
Debug("Narrowing %S -> %S (same) = %S.", &tstr0, &tstr1, &str);
#endif
return true;
}
else if constexpr (std::is_floating_point<FromType>::value && std::is_signed<ToType>::value) {
using signed_t = int64_t;
#ifdef _NARROW_PRINT_VERBOSE
auto tstr0 = Meta::name<FromType>();
auto tstr1 = Meta::name<ToType>();
auto tstr2 = Meta::name<signed_t>();
Debug("Narrowing %S -> %S | converting to %S and comparing (fs) = %S.", &tstr0, &tstr1, &tstr2, &str);
#endif
return static_cast<signed_t>(value) >= static_cast<signed_t>(std::numeric_limits<To>::min())
&& static_cast<signed_t>(value) <= static_cast<signed_t>(std::numeric_limits<To>::max());
}
else if constexpr (std::is_floating_point<FromType>::value && std::is_unsigned<ToType>::value) {
using unsigned_t = uint64_t;
#ifdef _NARROW_PRINT_VERBOSE
auto tstr0 = Meta::name<FromType>();
auto tstr1 = Meta::name<ToType>();
auto tstr2 = Meta::name<unsigned_t>();
Debug("Narrowing %S -> %S | converting to %S and comparing (fs) = %S.", &tstr0, &tstr1, &tstr2, &str);
#endif
return value >= FromType(0)
&& static_cast<unsigned_t>(value) <= static_cast<unsigned_t>(std::numeric_limits<To>::max());
}
else if constexpr (std::is_unsigned<FromType>::value && std::is_signed<ToType>::value) {
// unsigned to signed // unsigned to signed
using signed_t = int64_t; using signed_t = int64_t;
out_ranged = static_cast<signed_t>(value) > static_cast<signed_t>(std::numeric_limits<To>::max()); #ifdef _NARROW_PRINT_VERBOSE
auto tstr0 = Meta::name<FromType>();
} else { auto tstr1 = Meta::name<ToType>();
auto tstr2 = Meta::name<signed_t>();
Debug("Narrowing %S -> %S | converting to %S and comparing (us) = %S.", &tstr0, &tstr1, &tstr2, &str);
#endif
return static_cast<signed_t>(value) < static_cast<signed_t>(std::numeric_limits<To>::max());
}
else {
static_assert(std::is_signed<FromType>::value && std::is_unsigned<ToType>::value, "Expecting signed to unsigned conversion"); static_assert(std::is_signed<FromType>::value && std::is_unsigned<ToType>::value, "Expecting signed to unsigned conversion");
// signed to unsigned // signed to unsigned
using unsigned_t = typename std::make_unsigned<FromType>::type; using unsigned_t = typename try_make_unsigned<FromType>::type;
out_ranged = value < 0 || static_cast<unsigned_t>(value) > static_cast<unsigned_t>(std::numeric_limits<To>::max()); #ifdef _NARROW_PRINT_VERBOSE
auto tstr0 = Meta::name<FromType>();
auto tstr1 = Meta::name<ToType>();
auto tstr2 = Meta::name<unsigned_t>();
Debug("Narrowing %S -> %S | converting to %S and comparing (su) = %S.", &tstr0, &tstr1, &tstr2, &str);
#endif
return value >= 0 && static_cast<unsigned_t>(value) <= static_cast<unsigned_t>(std::numeric_limits<To>::max());
} }
}
if(out_ranged) {
template<typename To, typename From>
constexpr To narrow_cast(From&& value, struct tag::warn_on_error) {
if (!check_narrow_cast<To, From>(value)) {
auto vstr = Meta::toStr(value); auto vstr = Meta::toStr(value);
auto lstr = Meta::toStr(std::numeric_limits<To>::min()); auto lstr = Meta::toStr(std::numeric_limits<To>::min());
auto rstr = Meta::toStr(std::numeric_limits<To>::max()); auto rstr = Meta::toStr(std::numeric_limits<To>::max());
auto tstr = Meta::name<To>(); auto tstr = Meta::name<To>();
auto fstr = Meta::name<From>(); auto fstr = Meta::name<From>();
Warning("Value '%S' in narrowing conversion of %S -> %S is not within limits [%S,%S].", &vstr, &fstr, &tstr, &lstr, &rstr); Warning("Value '%S' in narrowing conversion of %S -> %S is not within limits [%S,%S].", &vstr, &fstr, &tstr, &lstr, &rstr);
} }
return static_cast<To>(std::forward<From>(value)); return static_cast<To>(std::forward<From>(value));
} }
template<typename To, typename From> template<typename To, typename From>
constexpr To narrow_cast(From&& value, struct tag::fail_on_error) { constexpr To narrow_cast(From&& value, struct tag::fail_on_error) {
using FromType = typename remove_cvref<From>::type; if (!check_narrow_cast<To, From>(value)) {
using ToType = typename remove_cvref<To>::type;
static_assert(sizeof(FromType) > sizeof(ToType) || (std::is_unsigned<FromType>::value && std::is_signed<ToType>::value), "From type has to be bigger than to type.");
//static_assert(std::numeric_limits<ToType>::max() <= std::numeric_limits<FromType>::max(), "Maximum numeric limits of To type are larger than From type.");
//static_assert(std::numeric_limits<ToType>::min() >= std::numeric_limits<FromType>::min(), "Minimum numeric limits of To type are smaller than From type.");
bool out_ranged = value > (FromType)std::numeric_limits<To>::max()
|| value < (FromType)std::numeric_limits<To>::min();
if constexpr(
std::is_floating_point<FromType>::value
|| (std::is_unsigned<FromType>::value && std::is_unsigned<ToType>::value)
|| (std::is_signed<FromType>::value && std::is_signed<ToType>::value)
) {
// unsigned to unsigned
} else if constexpr(std::is_unsigned<FromType>::value && std::is_signed<ToType>::value) {
// unsigned to signed
using signed_t = int64_t;
out_ranged = static_cast<signed_t>(value) > static_cast<signed_t>(std::numeric_limits<To>::max());
} else {
static_assert(std::is_signed<FromType>::value && std::is_unsigned<ToType>::value, "Expecting signed to unsigned conversion");
// signed to unsigned
using unsigned_t = typename std::make_unsigned<FromType>::type;
out_ranged = value < 0 || static_cast<unsigned_t>(value) > static_cast<unsigned_t>(std::numeric_limits<To>::max());
}
if(out_ranged) {
auto vstr = Meta::toStr(value); auto vstr = Meta::toStr(value);
auto lstr = Meta::toStr(std::numeric_limits<To>::min()); auto lstr = Meta::toStr(std::numeric_limits<To>::min());
auto rstr = Meta::toStr(std::numeric_limits<To>::max()); auto rstr = Meta::toStr(std::numeric_limits<To>::max());
auto tstr = Meta::name<To>(); auto tstr = Meta::name<To>();
auto fstr = Meta::name<From>(); auto fstr = Meta::name<From>();
U_EXCEPTION("Value '%S' in narrowing conversion of %S -> %S is not within limits [%S,%S].", &vstr, &fstr, &tstr, &lstr, &rstr); U_EXCEPTION("Value '%S' in narrowing conversion of %S -> %S is not within limits [%S,%S].", &vstr, &fstr, &tstr, &lstr, &rstr);
} }
return static_cast<To>(std::forward<From>(value)); return static_cast<To>(std::forward<From>(value));
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment