#pragma once #include "typetraits.h" #include "yexception.h" #include <util/system/compat.h> #include <util/system/type_name.h> #include <util/system/unaligned_mem.h> #include <util/system/yassert.h> #include <cstdlib> template <class T, class F> static inline T VerifyDynamicCast(F f) { if (!f) { return nullptr; } T ret = dynamic_cast<T>(f); Y_VERIFY(ret, "verify cast failed"); return ret; } #if !defined(NDEBUG) #define USE_DEBUG_CHECKED_CAST #endif namespace NPrivate { template <typename T, typename F> static T DynamicCast(F f) { return dynamic_cast<T>(f); } } /* * replacement for dynamic_cast(dynamic_cast in debug mode, else static_cast) */ template <class T, class F> static inline T CheckedCast(F f) { #if defined(USE_DEBUG_CHECKED_CAST) return VerifyDynamicCast<T>(f); #else /* Make sure F is polymorphic. * Without this cast, CheckedCast with non-polymorphic F * incorrectly compiled without error in release mode. */ { auto&& x = &::NPrivate::DynamicCast<T, F>; (void)x; } return static_cast<T>(f); #endif // USE_DEBUG_CHECKED_CAST } /* * be polite */ #undef USE_DEBUG_CHECKED_CAST template <bool isUnsigned> class TInteger; template <> class TInteger<true> { public: template <class TUnsigned> static constexpr bool IsNegative(TUnsigned) noexcept { return false; } }; template <> class TInteger<false> { public: template <class TSigned> static constexpr bool IsNegative(const TSigned value) noexcept { return value < 0; } }; template <class TType> constexpr bool IsNegative(const TType value) noexcept { return TInteger<std::is_unsigned<TType>::value>::IsNegative(value); } namespace NPrivate { template <class T> using TUnderlyingTypeOrSelf = typename std::conditional< std::is_enum<T>::value, std::underlying_type<T>, // Lazy evaluatuion: do not call ::type here, because underlying_type<T> is undefined if T is not an enum. std::enable_if<true, T> // Wrapping T in a class, that has member ::type typedef. >::type::type; // Left ::type is for std::conditional, right ::type is for underlying_type/enable_if template <class TSmall, class TLarge> struct TSafelyConvertible { using TSmallInt = TUnderlyingTypeOrSelf<TSmall>; using TLargeInt = TUnderlyingTypeOrSelf<TLarge>; static constexpr bool Result = std::is_integral<TSmallInt>::value && std::is_integral<TLargeInt>::value && ((std::is_signed<TSmallInt>::value == std::is_signed<TLargeInt>::value && sizeof(TSmallInt) >= sizeof(TLargeInt)) || (std::is_signed<TSmallInt>::value && sizeof(TSmallInt) > sizeof(TLargeInt))); }; } template <class TSmallInt, class TLargeInt> constexpr std::enable_if_t<::NPrivate::TSafelyConvertible<TSmallInt, TLargeInt>::Result, TSmallInt> SafeIntegerCast(TLargeInt largeInt) noexcept { return static_cast<TSmallInt>(largeInt); } template <class TSmall, class TLarge> inline std::enable_if_t<!::NPrivate::TSafelyConvertible<TSmall, TLarge>::Result, TSmall> SafeIntegerCast(TLarge largeInt) { using TSmallInt = ::NPrivate::TUnderlyingTypeOrSelf<TSmall>; using TLargeInt = ::NPrivate::TUnderlyingTypeOrSelf<TLarge>; if (std::is_unsigned<TSmallInt>::value && std::is_signed<TLargeInt>::value) { if (IsNegative(largeInt)) { ythrow TBadCastException() << "Conversion '" << TypeName<TLarge>() << '{' << TLargeInt(largeInt) << "}' to '" << TypeName<TSmallInt>() << "', negative value converted to unsigned"; } } TSmallInt smallInt = TSmallInt(largeInt); if (std::is_signed<TSmallInt>::value && std::is_unsigned<TLargeInt>::value) { if (IsNegative(smallInt)) { ythrow TBadCastException() << "Conversion '" << TypeName<TLarge>() << '{' << TLargeInt(largeInt) << "}' to '" << TypeName<TSmallInt>() << "', positive value converted to negative"; } } if (TLargeInt(smallInt) != largeInt) { ythrow TBadCastException() << "Conversion '" << TypeName<TLarge>() << '{' << TLargeInt(largeInt) << "}' to '" << TypeName<TSmallInt>() << "', loss of data"; } return static_cast<TSmall>(smallInt); } template <class TSmallInt, class TLargeInt> inline TSmallInt IntegerCast(TLargeInt largeInt) noexcept { try { return SafeIntegerCast<TSmallInt>(largeInt); } catch (const yexception& exc) { Y_FAIL("IntegerCast: %s", exc.what()); } } /* Convert given enum value to its underlying type. This is just a shortcut for * `static_cast<std::underlying_type_t<EEnum>>(enum_)`. */ template <typename T> constexpr std::underlying_type_t<T> ToUnderlying(const T enum_) noexcept { return static_cast<std::underlying_type_t<T>>(enum_); } // std::bit_cast from c++20 template <class TTarget, class TSource> TTarget BitCast(const TSource& source) { static_assert(sizeof(TSource) == sizeof(TTarget), "Size mismatch"); static_assert(std::is_trivially_copyable<TSource>::value, "TSource is not trivially copyable"); static_assert(std::is_trivial<TTarget>::value, "TTarget is not trivial"); // Support volatile qualifiers. // ReadUnaligned does not work with volatile pointers, so cast away // volatileness beforehand. using TNonvolatileSource = std::remove_volatile_t<TSource>; using TNonvolatileTarget = std::remove_volatile_t<TTarget>; return ReadUnaligned<TNonvolatileTarget>(&const_cast<const TNonvolatileSource&>(source)); }