diff options
author | babenko <babenko@yandex-team.com> | 2024-11-03 15:01:37 +0300 |
---|---|---|
committer | babenko <babenko@yandex-team.com> | 2024-11-03 15:13:18 +0300 |
commit | 1d9c1fe4155c9fbc93a2b08754643f38b2d8a9e6 (patch) | |
tree | 4304bf131c70c48c008b0823149420636f213b46 /library | |
parent | 516aec4353c6dc63d05883924b1f29d327db6413 (diff) | |
download | ydb-1d9c1fe4155c9fbc93a2b08754643f38b2d8a9e6.tar.gz |
YT-22885: Refactor checked casts
commit_hash:7f7600d332c3ddb5c8372e921bcba3b4fbed68f8
Diffstat (limited to 'library')
-rw-r--r-- | library/cpp/yt/misc/arcadia_enum-inl.h | 12 | ||||
-rw-r--r-- | library/cpp/yt/misc/cast-inl.h | 55 | ||||
-rw-r--r-- | library/cpp/yt/misc/cast.h | 10 | ||||
-rw-r--r-- | library/cpp/yt/misc/enum-inl.h | 10 | ||||
-rw-r--r-- | library/cpp/yt/misc/enum.h | 2 | ||||
-rw-r--r-- | library/cpp/yt/misc/unittests/cast_ut.cpp | 45 | ||||
-rw-r--r-- | library/cpp/yt/misc/unittests/enum_ut.cpp | 38 | ||||
-rw-r--r-- | library/cpp/yt/misc/unittests/ya.make | 1 |
8 files changed, 122 insertions, 51 deletions
diff --git a/library/cpp/yt/misc/arcadia_enum-inl.h b/library/cpp/yt/misc/arcadia_enum-inl.h index 17a10bb3b2..1df8b2c464 100644 --- a/library/cpp/yt/misc/arcadia_enum-inl.h +++ b/library/cpp/yt/misc/arcadia_enum-inl.h @@ -42,6 +42,18 @@ struct TArcadiaEnumTraitsImpl auto it = LiteralToValue.find(literal); return it == LiteralToValue.end() ? std::nullopt : std::make_optional(it->second); } + + static bool IsKnownValue(T value) + { + static const auto Values = [] { + THashSet<T> result; + for (const auto& [value, _] : GetEnumNames<T>()) { + result.insert(value); + } + return result; + }(); + return Values.contains(value); + } }; //////////////////////////////////////////////////////////////////////////////// diff --git a/library/cpp/yt/misc/cast-inl.h b/library/cpp/yt/misc/cast-inl.h index 00e6e240b8..50920f0193 100644 --- a/library/cpp/yt/misc/cast-inl.h +++ b/library/cpp/yt/misc/cast-inl.h @@ -94,20 +94,19 @@ constexpr bool IsInIntegralRange(S value) } template <class T, class S> -constexpr bool TryIntegralCast(S value, T* result) +constexpr std::optional<T> TryCheckedIntegralCast(S value) { - if (!NDetail::IsInIntegralRange<T>(value)) { - return false; + [[unlikely]] if (!NDetail::IsInIntegralRange<T>(value)) { + return std::nullopt; } - *result = static_cast<T>(value); - return true; + return static_cast<T>(value); } template <class T, class S> T CheckedIntegralCast(S value) { - T result; - if (!TryIntegralCast<T>(value, &result)) { + auto result = TryCheckedIntegralCast<T>(value); + if (!result) { throw TSimpleException(Sprintf("Error casting %s value \"%s\" to %s: value is out of expected range [%s; %s]", TypeName<S>().c_str(), NYT::NDetail::FormatInvalidCastValue(value).c_str(), @@ -115,35 +114,43 @@ T CheckedIntegralCast(S value) ::ToString(std::numeric_limits<T>::lowest()).c_str(), ::ToString(std::numeric_limits<T>::max()).c_str())); } - return result; + return *result; } template <class T, class S> -constexpr bool TryEnumCast(S value, T* result) + requires TEnumTraits<T>::IsEnum +constexpr std::optional<T> TryCheckedEnumCast(S value) { - std::underlying_type_t<T> underlying; - if (!TryIntegralCast<std::underlying_type_t<T>>(value, &underlying)) { - return false; + auto underlying = TryCheckedIntegralCast<std::underlying_type_t<T>>(value); + [[unlikely]] if (!underlying) { + return std::nullopt; } - auto candidate = static_cast<T>(underlying); - if (!TEnumTraits<T>::FindLiteralByValue(candidate)) { - return false; + auto candidate = static_cast<T>(*underlying); + [[unlikely]] if (!TEnumTraits<T>::IsValidValue(candidate)) { + return std::nullopt; } - *result = candidate; - return true; + return candidate; } template <class T, class S> + requires TEnumTraits<T>::IsEnum T CheckedEnumCast(S value) { - T result; - if (!TryEnumCast<T>(value, &result)) { - throw TSimpleException(Sprintf("Error casting %s value \"%d\" to enum %s", - TypeName<S>().c_str(), - static_cast<int>(value), - TEnumTraits<T>::GetTypeName().data())); + auto result = TryCheckedEnumCast<T>(value); + [[unlikely]] if (!result) { + if constexpr (std::is_signed_v<S>) { + throw TSimpleException(Sprintf("Error casting %s value \"%" PRIi64 "\" to enum %s", + TypeName<S>().c_str(), + static_cast<i64>(value), + TEnumTraits<T>::GetTypeName().data())); + } else { + throw TSimpleException(Sprintf("Error casting %s value \"%" PRIu64 "\" to enum %s", + TypeName<S>().c_str(), + static_cast<ui64>(value), + TEnumTraits<T>::GetTypeName().data())); + } } - return result; + return *result; } //////////////////////////////////////////////////////////////////////////////// diff --git a/library/cpp/yt/misc/cast.h b/library/cpp/yt/misc/cast.h index e8654cc122..a4e32e0287 100644 --- a/library/cpp/yt/misc/cast.h +++ b/library/cpp/yt/misc/cast.h @@ -1,7 +1,11 @@ #pragma once +#include "enum.h" + #include <library/cpp/yt/exception/exception.h> +#include <optional> + namespace NYT { //////////////////////////////////////////////////////////////////////////////// @@ -13,7 +17,7 @@ template <class T, class S> constexpr bool IsInIntegralRange(S value); template <class T, class S> -constexpr bool TryIntegralCast(S value, T* result); +constexpr std::optional<T> TryCheckedIntegralCast(S value); template <class T, class S> T CheckedIntegralCast(S value); @@ -21,9 +25,11 @@ T CheckedIntegralCast(S value); //////////////////////////////////////////////////////////////////////////////// template <class T, class S> -constexpr bool TryEnumCast(S value, T* result); + requires TEnumTraits<T>::IsEnum +constexpr std::optional<T> TryCheckedEnumCast(S value); template <class T, class S> + requires TEnumTraits<T>::IsEnum T CheckedEnumCast(S value); //////////////////////////////////////////////////////////////////////////////// diff --git a/library/cpp/yt/misc/enum-inl.h b/library/cpp/yt/misc/enum-inl.h index f1fb9f63e1..d8037bbf74 100644 --- a/library/cpp/yt/misc/enum-inl.h +++ b/library/cpp/yt/misc/enum-inl.h @@ -324,6 +324,16 @@ constexpr bool TEnumTraits<T, true>::IsKnownValue(T value) } template <class T> +constexpr bool TEnumTraits<T, true>::IsValidValue(T value) +{ + if constexpr (IsBitEnum) { + return (value & TEnumTraits<T>::GetAllSetValue()) == value; + } else { + return IsKnownValue(value); + } +} + +template <class T> TString TEnumTraits<T, true>::ToString(T value) { using ::ToString; diff --git a/library/cpp/yt/misc/enum.h b/library/cpp/yt/misc/enum.h index d9611b2823..331eb59f1a 100644 --- a/library/cpp/yt/misc/enum.h +++ b/library/cpp/yt/misc/enum.h @@ -89,8 +89,10 @@ struct TEnumTraits<T, true> static constexpr std::optional<T> TryGetUnknownValue(); static std::optional<TStringBuf> FindLiteralByValue(T value); static std::optional<T> FindValueByLiteral(TStringBuf literal); + static constexpr bool IsKnownValue(T value) requires (!TEnumTraitsImpl<T>::IsBitEnum); + static constexpr bool IsValidValue(T value); static TString ToString(T value); static T FromString(TStringBuf literal); diff --git a/library/cpp/yt/misc/unittests/cast_ut.cpp b/library/cpp/yt/misc/unittests/cast_ut.cpp new file mode 100644 index 0000000000..8662575d6c --- /dev/null +++ b/library/cpp/yt/misc/unittests/cast_ut.cpp @@ -0,0 +1,45 @@ +#include <library/cpp/testing/gtest/gtest.h> + +#include <library/cpp/yt/misc/cast.h> +#include <library/cpp/yt/misc/enum.h> + +namespace NYT { +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +DEFINE_ENUM_WITH_UNDERLYING_TYPE(ECardinal, char, + ((West) (0)) + ((North) (1)) + ((East) (2)) + ((South) (3)) +); + +DEFINE_BIT_ENUM_WITH_UNDERLYING_TYPE(EFeatures, ui8, + ((None) (0x0000)) + ((First) (0x0001)) + ((Second)(0x0002)) +); + +TEST(TCastTest, TryCheckedEnumCast) +{ + EXPECT_EQ((TryCheckedEnumCast<ECardinal, char>(2)), ECardinal::East); + EXPECT_EQ((TryCheckedEnumCast<ECardinal, int>(3)), ECardinal::South); + + EXPECT_FALSE((TryCheckedEnumCast<ECardinal, char>(100))); + EXPECT_FALSE((TryCheckedEnumCast<ECardinal, int>(300))); + + EXPECT_EQ((TryCheckedEnumCast<EFeatures, ui8>(0)), EFeatures::None); + EXPECT_EQ((TryCheckedEnumCast<EFeatures, ui8>(ToUnderlying(EFeatures::First))), EFeatures::First); + EXPECT_EQ((TryCheckedEnumCast<EFeatures, ui8>(ToUnderlying(EFeatures::Second))), EFeatures::Second); + EXPECT_EQ((TryCheckedEnumCast<EFeatures, int>(ToUnderlying(EFeatures::First))), EFeatures::First); + EXPECT_EQ((TryCheckedEnumCast<EFeatures, ui8>(ToUnderlying(EFeatures::First | EFeatures::Second))), EFeatures::First | EFeatures::Second); + + EXPECT_FALSE((TryCheckedEnumCast<EFeatures, ui8>(0x10))); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace +} // namespace NYT + diff --git a/library/cpp/yt/misc/unittests/enum_ut.cpp b/library/cpp/yt/misc/unittests/enum_ut.cpp index fc7feed227..22bfe1fdc9 100644 --- a/library/cpp/yt/misc/unittests/enum_ut.cpp +++ b/library/cpp/yt/misc/unittests/enum_ut.cpp @@ -215,10 +215,21 @@ TEST(TEnumTest, IsKnownValue) EXPECT_TRUE(TEnumTraits<ESimple>::IsKnownValue(ESimple::X)); EXPECT_TRUE(TEnumTraits<ESimple>::IsKnownValue(ESimple::Y)); EXPECT_TRUE(TEnumTraits<ESimple>::IsKnownValue(ESimple::Z)); - EXPECT_FALSE(TEnumTraits<ESimple>::IsKnownValue(static_cast<ESimple>(100))); +} - EXPECT_TRUE(TEnumTraits<EColor>::IsKnownValue(EColor::Red)); +TEST(TEnumTest, IsValidValue) +{ + EXPECT_TRUE(TEnumTraits<ESimple>::IsValidValue(ESimple::X)); + EXPECT_TRUE(TEnumTraits<ESimple>::IsValidValue(ESimple::Y)); + EXPECT_TRUE(TEnumTraits<ESimple>::IsValidValue(ESimple::Z)); + EXPECT_FALSE(TEnumTraits<ESimple>::IsValidValue(static_cast<ESimple>(100))); + + EXPECT_TRUE(TEnumTraits<EFlag>::IsValidValue(EFlag())); + EXPECT_TRUE(TEnumTraits<EFlag>::IsValidValue(EFlag::_1)); + EXPECT_TRUE(TEnumTraits<EFlag>::IsValidValue(EFlag::_1 | EFlag::_2)); + EXPECT_TRUE(TEnumTraits<EFlag>::IsValidValue(EFlag::_1 | EFlag::_2 | EFlag::_3 | EFlag::_4)); + EXPECT_FALSE(TEnumTraits<EFlag>::IsValidValue(static_cast<EFlag>(0x10))); } TEST(TEnumTest, AllSetValue) @@ -280,29 +291,6 @@ TEST(TEnumTest, CustomString) EXPECT_EQ("1_b", ToString(ECustomString::B)); } -TEST(TEnumTest, Cast) -{ - ECardinal cardinal; - { - char validValue = 2; - EXPECT_TRUE(TryEnumCast(validValue, &cardinal)); - EXPECT_EQ(cardinal, ECardinal::East); - } - { - char invalidValue = 100; - EXPECT_FALSE(TryEnumCast(invalidValue, &cardinal)); - } - { - int widerTypeValidValue = 3; - EXPECT_TRUE(TryEnumCast(widerTypeValidValue, &cardinal)); - EXPECT_EQ(cardinal, ECardinal::South); - } - { - int widerTypeInvalueValue = (1 << 8) + 100; - EXPECT_FALSE(TryEnumCast(widerTypeInvalueValue, &cardinal)); - } -} - TEST(TEnumTest, UnknownValue) { EXPECT_EQ(TEnumTraits<EColor>::TryGetUnknownValue(), std::nullopt); diff --git a/library/cpp/yt/misc/unittests/ya.make b/library/cpp/yt/misc/unittests/ya.make index 5a071c887c..aa6d182c4b 100644 --- a/library/cpp/yt/misc/unittests/ya.make +++ b/library/cpp/yt/misc/unittests/ya.make @@ -3,6 +3,7 @@ GTEST(unittester-library-misc) INCLUDE(${ARCADIA_ROOT}/library/cpp/yt/ya_cpp.make.inc) SRCS( + cast_ut.cpp enum_ut.cpp guid_ut.cpp non_null_ptr_ut.cpp |