diff options
| author | kshalnev <[email protected]> | 2025-03-27 09:05:01 +0300 |
|---|---|---|
| committer | kshalnev <[email protected]> | 2025-03-27 09:20:00 +0300 |
| commit | 54394ecb8d819f075dddd15ed8cdb4fb822d66cd (patch) | |
| tree | 17e481976ebfee5cff047cdf5773844199c72a20 | |
| parent | 6f57592a3b6099c881ca20f5b54520a3852a1d56 (diff) | |
Added SafeCastToEnum
commit_hash:44102d9b2c000eefdfd91997a40a055b29d647c7
| -rw-r--r-- | util/generic/enum_cast.cpp | 12 | ||||
| -rw-r--r-- | util/generic/enum_cast.h | 46 | ||||
| -rw-r--r-- | util/generic/enum_cast_ut.cpp | 49 | ||||
| -rw-r--r-- | util/generic/enum_cast_ut.h | 28 | ||||
| -rw-r--r-- | util/generic/ut/ya.make | 3 | ||||
| -rw-r--r-- | util/ya.make | 1 |
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 |
