summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkshalnev <[email protected]>2025-03-27 09:05:01 +0300
committerkshalnev <[email protected]>2025-03-27 09:20:00 +0300
commit54394ecb8d819f075dddd15ed8cdb4fb822d66cd (patch)
tree17e481976ebfee5cff047cdf5773844199c72a20
parent6f57592a3b6099c881ca20f5b54520a3852a1d56 (diff)
Added SafeCastToEnum
commit_hash:44102d9b2c000eefdfd91997a40a055b29d647c7
-rw-r--r--util/generic/enum_cast.cpp12
-rw-r--r--util/generic/enum_cast.h46
-rw-r--r--util/generic/enum_cast_ut.cpp49
-rw-r--r--util/generic/enum_cast_ut.h28
-rw-r--r--util/generic/ut/ya.make3
-rw-r--r--util/ya.make1
6 files changed, 139 insertions, 0 deletions
diff --git a/util/generic/enum_cast.cpp b/util/generic/enum_cast.cpp
new file mode 100644
index 00000000000..8db625ef440
--- /dev/null
+++ b/util/generic/enum_cast.cpp
@@ -0,0 +1,12 @@
+#include "enum_cast.h"
+
+#include <util/generic/yexception.h>
+#include <util/system/type_name.h>
+
+namespace NPrivate {
+
+ [[noreturn]] void OnSafeCastToEnumUnexpectedValue(const std::type_info& valueTypeInfo) {
+ ythrow TBadCastException() << "Unexpected enum " << TypeName(valueTypeInfo) << " value";
+ }
+
+} // namespace NPrivate
diff --git a/util/generic/enum_cast.h b/util/generic/enum_cast.h
new file mode 100644
index 00000000000..59dee47e889
--- /dev/null
+++ b/util/generic/enum_cast.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <util/generic/cast.h>
+#include <util/generic/serialized_enum.h>
+
+#include <optional>
+#include <type_traits>
+#include <typeinfo>
+
+namespace NPrivate {
+
+ [[noreturn]] void OnSafeCastToEnumUnexpectedValue(const std::type_info& valueTypeInfo);
+
+} // namespace NPrivate
+
+/**
+ * Safely cast an integer value to the enum value.
+ * @throw yexception is case of unknown enum underlying type value
+ *
+ * @tparam TEnum enum type
+ */
+template <typename TEnum, typename TInteger, typename = std::enable_if_t<std::is_enum_v<TEnum>>>
+TEnum SafeCastToEnum(TInteger integerValue) {
+ using TUnderlyingEnumType = std::underlying_type_t<TEnum>;
+
+ std::optional<TUnderlyingEnumType> value;
+ try {
+ value = SafeIntegerCast<TUnderlyingEnumType>(integerValue);
+ } catch (const TBadCastException&) {
+ // SafeIntegerCast throws TBadCastException when TInteger cannot be cast
+ // to TUnderlyingEnumType but the exception message is about integer
+ // value cast being unsafe.
+ // SafeCastToEnum must throw TBadCastException with its own exception
+ // message even if integer cast fails.
+ }
+
+ if (value.has_value()) {
+ for (TEnum enumValue : GetEnumAllValues<TEnum>()) {
+ if (static_cast<TUnderlyingEnumType>(enumValue) == *value) {
+ return enumValue;
+ }
+ }
+ }
+
+ NPrivate::OnSafeCastToEnumUnexpectedValue(typeid(TEnum));
+}
diff --git a/util/generic/enum_cast_ut.cpp b/util/generic/enum_cast_ut.cpp
new file mode 100644
index 00000000000..0b96235bcaf
--- /dev/null
+++ b/util/generic/enum_cast_ut.cpp
@@ -0,0 +1,49 @@
+#include "enum_cast.h"
+
+#include "enum_cast_ut.h"
+
+#include <library/cpp/testing/unittest/registar.h>
+
+Y_UNIT_TEST_SUITE(TestEnumCast) {
+ Y_UNIT_TEST(SafeCastToEnumTest) {
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<EIntEnum>(0), EIntEnum::Zero);
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<EIntEnum>(1), EIntEnum::One);
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<EIntEnum>(2), EIntEnum::Two);
+ UNIT_ASSERT_EXCEPTION(SafeCastToEnum<EIntEnum>(3), TBadCastException);
+
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<EUcharEnum>(0), EUcharEnum::Zero);
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<EUcharEnum>(1), EUcharEnum::One);
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<EUcharEnum>(2), EUcharEnum::Two);
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ SafeCastToEnum<EUcharEnum>(3), TBadCastException,
+ "Unexpected enum");
+ int val1 = 256;
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ SafeCastToEnum<EUcharEnum>(val1), TBadCastException,
+ "Unexpected enum");
+ int val2 = -1;
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ SafeCastToEnum<EUcharEnum>(val2), TBadCastException,
+ "Unexpected enum");
+ int val3 = 2;
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<EUcharEnum>(val3), EUcharEnum::Two);
+
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<EBoolEnum>(false), EBoolEnum::False);
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<EBoolEnum>(true), EBoolEnum::True);
+
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<EUnscopedIntEnum>(2), UIE_TWO);
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ SafeCastToEnum<EUnscopedIntEnum>(3), TBadCastException,
+ "Unexpected enum");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ SafeCastToEnum<EUnscopedIntEnum>(9), TBadCastException,
+ "Unexpected enum");
+
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<ECharEnum>(static_cast<unsigned int>(0)), ECharEnum::Zero);
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<ECharEnum>(static_cast<short>(-1)), ECharEnum::MinusOne);
+ UNIT_ASSERT_VALUES_EQUAL(SafeCastToEnum<ECharEnum>(static_cast<int>(-2)), ECharEnum::MinusTwo);
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ SafeCastToEnum<ECharEnum>(static_cast<int>(2)), TBadCastException,
+ "Unexpected enum");
+ }
+} // Y_UNIT_TEST_SUITE(TestEnumCast)
diff --git a/util/generic/enum_cast_ut.h b/util/generic/enum_cast_ut.h
new file mode 100644
index 00000000000..c0fb52864aa
--- /dev/null
+++ b/util/generic/enum_cast_ut.h
@@ -0,0 +1,28 @@
+#pragma once
+
+enum class EIntEnum: int {
+ Zero = 0,
+ One = 1,
+ Two = 2
+};
+
+enum class EUcharEnum: unsigned char {
+ Zero = 0,
+ One = 1,
+ Two = 2
+};
+
+enum class ECharEnum: signed char {
+ Zero = 0,
+ MinusOne = -1,
+ MinusTwo = -2
+};
+
+enum class EBoolEnum: bool {
+ False = false,
+ True = true
+};
+
+enum EUnscopedIntEnum {
+ UIE_TWO = 2,
+};
diff --git a/util/generic/ut/ya.make b/util/generic/ut/ya.make
index 6a605564fec..543b30278cd 100644
--- a/util/generic/ut/ya.make
+++ b/util/generic/ut/ya.make
@@ -12,6 +12,7 @@ SRCS(
generic/buffer_ut.cpp
generic/cast_ut.cpp
generic/deque_ut.cpp
+ generic/enum_cast_ut.cpp
generic/explicit_type_ut.cpp
generic/flags_ut.cpp
generic/function_ref_ut.cpp
@@ -63,4 +64,6 @@ PEERDIR(
library/cpp/containers/absl_flat_hash
)
+GENERATE_ENUM_SERIALIZATION(generic/enum_cast_ut.h)
+
END()
diff --git a/util/ya.make b/util/ya.make
index dea1f1f7219..93bcc964a48 100644
--- a/util/ya.make
+++ b/util/ya.make
@@ -90,6 +90,7 @@ JOIN_SRCS(
generic/buffer.cpp
generic/cast.cpp
generic/deque.cpp
+ generic/enum_cast.cpp
generic/explicit_type.cpp
generic/fastqueue.cpp
generic/flags.cpp