diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/generic/cast.h | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/generic/cast.h')
-rw-r--r-- | util/generic/cast.h | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/util/generic/cast.h b/util/generic/cast.h new file mode 100644 index 0000000000..0d4a41f385 --- /dev/null +++ b/util/generic/cast.h @@ -0,0 +1,176 @@ +#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)); +} |