diff --git a/Application/src/commons/common/misc/metastring.h b/Application/src/commons/common/misc/metastring.h index ca12fa3a558d21a257c0ab54aeb59d8f40ed4ced..e8c7784577707bca03cd5a026f1ad2b65b32ff4a 100644 --- a/Application/src/commons/common/misc/metastring.h +++ b/Application/src/commons/common/misc/metastring.h @@ -1040,91 +1040,116 @@ struct warn_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> -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 ToType = typename remove_cvref<To>::type; - if constexpr (sizeof(FromType) <= sizeof(ToType)) - return static_cast<ToType>(std::forward<From>(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."); - //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) - ) { + auto str = Meta::toStr(value); + if constexpr ( + std::is_floating_point<ToType>::value + || (std::is_signed<FromType>::value == std::is_signed<ToType>::value && !std::is_floating_point<FromType>::value) + ) + { // unsigned to unsigned - - } else if constexpr(std::is_unsigned<FromType>::value && std::is_signed<ToType>::value) { +#ifdef _NARROW_PRINT_VERBOSE + 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 using signed_t = int64_t; - out_ranged = static_cast<signed_t>(value) > static_cast<signed_t>(std::numeric_limits<To>::max()); - - } else { +#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 (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"); // 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()); + using unsigned_t = typename try_make_unsigned<FromType>::type; +#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 lstr = Meta::toStr(std::numeric_limits<To>::min()); auto rstr = Meta::toStr(std::numeric_limits<To>::max()); - + auto tstr = Meta::name<To>(); 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); } - + return static_cast<To>(std::forward<From>(value)); } template<typename To, typename From> constexpr To narrow_cast(From&& value, struct tag::fail_on_error) { - using FromType = typename remove_cvref<From>::type; - 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) { + if (!check_narrow_cast<To, From>(value)) { auto vstr = Meta::toStr(value); auto lstr = Meta::toStr(std::numeric_limits<To>::min()); auto rstr = Meta::toStr(std::numeric_limits<To>::max()); - + auto tstr = Meta::name<To>(); 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); } - + return static_cast<To>(std::forward<From>(value)); }