cast.h 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #pragma once
  2. #include "typetraits.h"
  3. #include "yexception.h"
  4. #include <util/system/compat.h>
  5. #include <util/system/type_name.h>
  6. #include <util/system/unaligned_mem.h>
  7. #include <util/system/yassert.h>
  8. #include <cstdlib>
  9. template <class T, class F>
  10. static inline T VerifyDynamicCast(F f) {
  11. if (!f) {
  12. return nullptr;
  13. }
  14. T ret = dynamic_cast<T>(f);
  15. Y_ABORT_UNLESS(ret, "verify cast failed");
  16. return ret;
  17. }
  18. #if !defined(NDEBUG)
  19. #define USE_DEBUG_CHECKED_CAST
  20. #endif
  21. namespace NPrivate {
  22. template <typename T, typename F>
  23. static T DynamicCast(F f) {
  24. return dynamic_cast<T>(f);
  25. }
  26. }
  27. /*
  28. * replacement for dynamic_cast(dynamic_cast in debug mode, else static_cast)
  29. */
  30. template <class T, class F>
  31. static inline T CheckedCast(F f) {
  32. #if defined(USE_DEBUG_CHECKED_CAST)
  33. return VerifyDynamicCast<T>(f);
  34. #else
  35. /* Make sure F is polymorphic.
  36. * Without this cast, CheckedCast with non-polymorphic F
  37. * incorrectly compiled without error in release mode.
  38. */
  39. {
  40. auto&& x = &::NPrivate::DynamicCast<T, F>;
  41. (void)x;
  42. }
  43. return static_cast<T>(f);
  44. #endif // USE_DEBUG_CHECKED_CAST
  45. }
  46. /*
  47. * be polite
  48. */
  49. #undef USE_DEBUG_CHECKED_CAST
  50. template <bool isUnsigned>
  51. class TInteger;
  52. template <>
  53. class TInteger<true> {
  54. public:
  55. template <class TUnsigned>
  56. static constexpr bool IsNegative(TUnsigned) noexcept {
  57. return false;
  58. }
  59. };
  60. template <>
  61. class TInteger<false> {
  62. public:
  63. template <class TSigned>
  64. static constexpr bool IsNegative(const TSigned value) noexcept {
  65. return value < 0;
  66. }
  67. };
  68. template <class TType>
  69. constexpr bool IsNegative(const TType value) noexcept {
  70. return TInteger<std::is_unsigned<TType>::value>::IsNegative(value);
  71. }
  72. namespace NPrivate {
  73. template <class T>
  74. using TUnderlyingTypeOrSelf = typename std::conditional<
  75. std::is_enum<T>::value,
  76. std::underlying_type<T>, // Lazy evaluatuion: do not call ::type here, because underlying_type<T> is undefined if T is not an enum.
  77. std::enable_if<true, T> // Wrapping T in a class, that has member ::type typedef.
  78. >::type::type; // Left ::type is for std::conditional, right ::type is for underlying_type/enable_if
  79. template <class TSmall, class TLarge>
  80. struct TSafelyConvertible {
  81. using TSmallInt = TUnderlyingTypeOrSelf<TSmall>;
  82. using TLargeInt = TUnderlyingTypeOrSelf<TLarge>;
  83. static constexpr bool Result = std::is_integral<TSmallInt>::value && std::is_integral<TLargeInt>::value &&
  84. ((std::is_signed<TSmallInt>::value == std::is_signed<TLargeInt>::value && sizeof(TSmallInt) >= sizeof(TLargeInt)) ||
  85. (std::is_signed<TSmallInt>::value && sizeof(TSmallInt) > sizeof(TLargeInt)));
  86. };
  87. }
  88. template <class TSmallInt, class TLargeInt>
  89. constexpr std::enable_if_t<::NPrivate::TSafelyConvertible<TSmallInt, TLargeInt>::Result, TSmallInt> SafeIntegerCast(TLargeInt largeInt) noexcept {
  90. return static_cast<TSmallInt>(largeInt);
  91. }
  92. template <class TSmall, class TLarge>
  93. inline std::enable_if_t<!::NPrivate::TSafelyConvertible<TSmall, TLarge>::Result, TSmall> SafeIntegerCast(TLarge largeInt) {
  94. using TSmallInt = ::NPrivate::TUnderlyingTypeOrSelf<TSmall>;
  95. using TLargeInt = ::NPrivate::TUnderlyingTypeOrSelf<TLarge>;
  96. if (std::is_unsigned<TSmallInt>::value && std::is_signed<TLargeInt>::value) {
  97. if (IsNegative(largeInt)) {
  98. ythrow TBadCastException() << "Conversion '" << TypeName<TLarge>() << '{' << TLargeInt(largeInt) << "}' to '"
  99. << TypeName<TSmallInt>()
  100. << "', negative value converted to unsigned";
  101. }
  102. }
  103. TSmallInt smallInt = TSmallInt(largeInt);
  104. if (std::is_signed<TSmallInt>::value && std::is_unsigned<TLargeInt>::value) {
  105. if (IsNegative(smallInt)) {
  106. ythrow TBadCastException() << "Conversion '" << TypeName<TLarge>() << '{' << TLargeInt(largeInt) << "}' to '"
  107. << TypeName<TSmallInt>()
  108. << "', positive value converted to negative";
  109. }
  110. }
  111. if (TLargeInt(smallInt) != largeInt) {
  112. ythrow TBadCastException() << "Conversion '" << TypeName<TLarge>() << '{' << TLargeInt(largeInt) << "}' to '"
  113. << TypeName<TSmallInt>() << "', loss of data";
  114. }
  115. return static_cast<TSmall>(smallInt);
  116. }
  117. template <class TSmallInt, class TLargeInt>
  118. inline TSmallInt IntegerCast(TLargeInt largeInt) noexcept {
  119. try {
  120. return SafeIntegerCast<TSmallInt>(largeInt);
  121. } catch (const yexception& exc) {
  122. Y_ABORT("IntegerCast: %s", exc.what());
  123. }
  124. }
  125. /* Convert given enum value to its underlying type. This is just a shortcut for
  126. * `static_cast<std::underlying_type_t<EEnum>>(enum_)`.
  127. */
  128. template <typename T>
  129. constexpr std::underlying_type_t<T> ToUnderlying(const T enum_) noexcept {
  130. return static_cast<std::underlying_type_t<T>>(enum_);
  131. }
  132. // std::bit_cast from c++20
  133. template <class TTarget, class TSource>
  134. TTarget BitCast(const TSource& source) {
  135. static_assert(sizeof(TSource) == sizeof(TTarget), "Size mismatch");
  136. static_assert(std::is_trivially_copyable<TSource>::value, "TSource is not trivially copyable");
  137. static_assert(std::is_trivial<TTarget>::value, "TTarget is not trivial");
  138. // Support volatile qualifiers.
  139. // ReadUnaligned does not work with volatile pointers, so cast away
  140. // volatileness beforehand.
  141. using TNonvolatileSource = std::remove_volatile_t<TSource>;
  142. using TNonvolatileTarget = std::remove_volatile_t<TTarget>;
  143. return ReadUnaligned<TNonvolatileTarget>(&const_cast<const TNonvolatileSource&>(source));
  144. }