123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- #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_ABORT_UNLESS(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_ABORT("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));
- }
|