aboutsummaryrefslogtreecommitdiffstats
path: root/library
diff options
context:
space:
mode:
authorbabenko <babenko@yandex-team.com>2024-11-03 15:01:37 +0300
committerbabenko <babenko@yandex-team.com>2024-11-03 15:13:18 +0300
commit1d9c1fe4155c9fbc93a2b08754643f38b2d8a9e6 (patch)
tree4304bf131c70c48c008b0823149420636f213b46 /library
parent516aec4353c6dc63d05883924b1f29d327db6413 (diff)
downloadydb-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.h12
-rw-r--r--library/cpp/yt/misc/cast-inl.h55
-rw-r--r--library/cpp/yt/misc/cast.h10
-rw-r--r--library/cpp/yt/misc/enum-inl.h10
-rw-r--r--library/cpp/yt/misc/enum.h2
-rw-r--r--library/cpp/yt/misc/unittests/cast_ut.cpp45
-rw-r--r--library/cpp/yt/misc/unittests/enum_ut.cpp38
-rw-r--r--library/cpp/yt/misc/unittests/ya.make1
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