diff options
author | chezzie <chezzie@yandex-team.com> | 2024-06-25 09:54:31 +0300 |
---|---|---|
committer | chezzie <chezzie@yandex-team.com> | 2024-06-25 10:06:56 +0300 |
commit | e166004e08f4ea69899364beaae07218fc351e95 (patch) | |
tree | 1d0f83ed69b4964ef39d2813b1c85a6e223c9b73 | |
parent | 509c9fc9e7b9c3b8be7307d72a4c966e5f9aa194 (diff) | |
download | ydb-e166004e08f4ea69899364beaae07218fc351e95.tar.gz |
Enum range to util
Изолированный хидер, не должен ничего поломать
Почему решил не завязываться и не расширять GENERATE_ENUM_SERIALIZATION[_WITH_HEADER):
1) Нет возможности добавлять метаданные для енамов объявленных в cpp
2) Нет возможности добавлять метаданные для енамов объявленных в anonymous namespace
3) Нет возможности использовать метаданные в том же хидере, где объявлен enum
В первую очередь будет использован в библиотеке range_containers - https://a.yandex-team.ru/arcadia/market/library/range_containers/
74cd8c66052608f1b6fec69c39cb0f868376fe0a
-rw-r--r-- | util/generic/enum_range.cpp | 1 | ||||
-rw-r--r-- | util/generic/enum_range.h | 72 | ||||
-rw-r--r-- | util/generic/enum_range_ut.cpp | 237 | ||||
-rw-r--r-- | util/generic/ut/ya.make | 1 | ||||
-rw-r--r-- | util/ya.make | 1 |
5 files changed, 312 insertions, 0 deletions
diff --git a/util/generic/enum_range.cpp b/util/generic/enum_range.cpp new file mode 100644 index 0000000000..c0a97177fe --- /dev/null +++ b/util/generic/enum_range.cpp @@ -0,0 +1 @@ +#include "enum_range.h" diff --git a/util/generic/enum_range.h b/util/generic/enum_range.h new file mode 100644 index 0000000000..a9f78a291e --- /dev/null +++ b/util/generic/enum_range.h @@ -0,0 +1,72 @@ +#pragma once + +#include <type_traits> + +// This tiny header is to define value ranges at compile time in enums and enum class/struct types. +// +// enum class E1 { +// A, +// B, +// C, +// }; +// Y_DEFINE_ENUM_MINMAX(E1, A, C); +// +// or +// +// enum class E2 { +// A, +// B, +// C, +// }; +// Y_DEFINE_ENUM_MAX(E2, C); +// +// Notes: +// * use Y_DEFINE_ENUM_MINMAX / Y_DEFINE_ENUM_MINMAX if your enum is defined in a namespace +// * use Y_DEFINE_ENUM_MINMAX_F / Y_DEFINE_ENUM_MINMAX_F if your enum is defined in a class/struct +// * use shortened version Y_DEFINE_ENUM_MAX / Y_DEFINE_ENUM_MAX_F if enum begin is 0 +// * add Y_DEFINE_xxx macro immediately after enum definition +// +// Usage examples: +// TEnumRange<E>::Min // min value of range in enum type +// TEnumRange<E>::Max // max value of range in enum type +// TEnumRange<E>::UnderlyingMin // min value of range in underlying type +// TEnumRange<E>::UnderlyingMax // max value of range in underlying type + +void _YRegisterEnumRange(...); + +namespace NDetail::NEnumRange { + + template <typename E, E _Min, E _Max> + struct TEnumRange { + static_assert(std::is_enum_v<E>, ""); + + using TEnum = E; + using TUnderlying = std::underlying_type_t<TEnum>; + + static constexpr TEnum Min = _Min; + static constexpr TEnum Max = _Max; + + static constexpr TUnderlying UnderlyingMin = static_cast<TUnderlying>(Min); + static constexpr TUnderlying UnderlyingMax = static_cast<TUnderlying>(Max); + + static_assert(UnderlyingMin <= UnderlyingMax, "Invalid enum range"); + }; +} + +#define Y_DEFINE_ENUM_MINMAX_IMPL(PREFIX, E, Min, Max) \ + PREFIX ::NDetail::NEnumRange::TEnumRange<E, Min, Max> _YRegisterEnumRange(const E*) + +#define Y_DEFINE_ENUM_MINMAX(E, Min, Max) \ + Y_DEFINE_ENUM_MINMAX_IMPL([[maybe_unused]], E, E::Min, E::Max) + +#define Y_DEFINE_ENUM_MAX(E, Max) \ + Y_DEFINE_ENUM_MINMAX_IMPL([[maybe_unused]], E, E{}, E::Max) + +#define Y_DEFINE_ENUM_MINMAX_FRIEND(E, Min, Max) \ + Y_DEFINE_ENUM_MINMAX_IMPL(friend, E, E::Min, E::Max) + +#define Y_DEFINE_ENUM_MAX_FRIEND(E, Max) \ + Y_DEFINE_ENUM_MINMAX_IMPL(friend, E, E{}, E::Max) + +template <typename E> +using TEnumRange = decltype(_YRegisterEnumRange(static_cast<E*>(nullptr))); diff --git a/util/generic/enum_range_ut.cpp b/util/generic/enum_range_ut.cpp new file mode 100644 index 0000000000..38caeae61b --- /dev/null +++ b/util/generic/enum_range_ut.cpp @@ -0,0 +1,237 @@ +#include "enum_range.h" + +#include <util/stream/output.h> +#include <util/system/defaults.h> +#include <library/cpp/testing/unittest/registar.h> + +class TEnumRangeTest: public TTestBase { + UNIT_TEST_SUITE(TEnumRangeTest); + UNIT_TEST(TestGlobalEnumRange) + UNIT_TEST(TestNamedNamespaceEnumRange) + UNIT_TEST(TestAnonNamespaceEnumRange) + UNIT_TEST(TestMemberEnumRange) + UNIT_TEST_SUITE_END(); + +protected: + void TestGlobalEnumRange(); + void TestNamedNamespaceEnumRange(); + void TestAnonNamespaceEnumRange(); + void TestMemberEnumRange(); +}; + +UNIT_TEST_SUITE_REGISTRATION(TEnumRangeTest); + +#define Y_DEFINE_ENUM_SERIALIZATION(Enum, Prefix) \ + Y_DECLARE_OUT_SPEC(inline, Enum, stream, value) { \ + switch (value) { \ + using enum Enum; \ + case Y_CAT(Prefix, 1): \ + stream << Y_STRINGIZE(Y_CAT(Prefix, 1)); \ + case Y_CAT(Prefix, 2): \ + stream << Y_STRINGIZE(Y_CAT(Prefix, 2)); \ + case Y_CAT(Prefix, 3): \ + stream << Y_STRINGIZE(Y_CAT(Prefix, 3)); \ + } \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +enum class EGlobal1 { + G11, + G12, + G13 +}; +Y_DEFINE_ENUM_MINMAX(EGlobal1, G11, G13); + +enum EGlobal2 { + G21 = 5, + G22, + G23 = 9 +}; +Y_DEFINE_ENUM_MINMAX(EGlobal2, G21, G23); + +enum class EGlobal3 { + G31, + G32, + G33 +}; +Y_DEFINE_ENUM_MAX(EGlobal3, G33); + +enum EGlobal4 { + G41, + G42, + G43 +}; +Y_DEFINE_ENUM_MAX(EGlobal4, G43); + +Y_DEFINE_ENUM_SERIALIZATION(EGlobal1, G1); +Y_DEFINE_ENUM_SERIALIZATION(EGlobal2, G2); +Y_DEFINE_ENUM_SERIALIZATION(EGlobal3, G3); +Y_DEFINE_ENUM_SERIALIZATION(EGlobal4, G4); + +void TEnumRangeTest::TestGlobalEnumRange() { + UNIT_ASSERT_VALUES_EQUAL(EGlobal1::G11, TEnumRange<EGlobal1>::Min); + UNIT_ASSERT_VALUES_EQUAL(EGlobal1::G13, TEnumRange<EGlobal1>::Max); + UNIT_ASSERT_VALUES_EQUAL(EGlobal2::G21, TEnumRange<EGlobal2>::Min); + UNIT_ASSERT_VALUES_EQUAL(EGlobal2::G23, TEnumRange<EGlobal2>::Max); + UNIT_ASSERT_VALUES_EQUAL(EGlobal3::G31, TEnumRange<EGlobal3>::Min); + UNIT_ASSERT_VALUES_EQUAL(EGlobal3::G33, TEnumRange<EGlobal3>::Max); + UNIT_ASSERT_VALUES_EQUAL(EGlobal4::G41, TEnumRange<EGlobal4>::Min); + UNIT_ASSERT_VALUES_EQUAL(EGlobal4::G43, TEnumRange<EGlobal4>::Max); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<EGlobal1>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<EGlobal1>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(5, TEnumRange<EGlobal2>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(9, TEnumRange<EGlobal2>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<EGlobal3>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<EGlobal3>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<EGlobal4>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<EGlobal4>::UnderlyingMax); +} + +namespace NNamespace { + enum class ENamed1 { + N11, + N12, + N13 + }; + Y_DEFINE_ENUM_MINMAX(ENamed1, N11, N13); + + enum ENamed2 { + N21 = 5, + N22, + N23 = 9 + }; + Y_DEFINE_ENUM_MINMAX(ENamed2, N21, N23); + + enum class ENamed3 { + N31, + N32, + N33 + }; + Y_DEFINE_ENUM_MAX(ENamed3, N33); + + enum ENamed4 { + N41, + N42, + N43 + }; + Y_DEFINE_ENUM_MAX(ENamed4, N43); +} + +Y_DEFINE_ENUM_SERIALIZATION(NNamespace::ENamed1, N1); +Y_DEFINE_ENUM_SERIALIZATION(NNamespace::ENamed2, N2); +Y_DEFINE_ENUM_SERIALIZATION(NNamespace::ENamed3, N3); +Y_DEFINE_ENUM_SERIALIZATION(NNamespace::ENamed4, N4); + +void TEnumRangeTest::TestNamedNamespaceEnumRange() { + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed1::N11, TEnumRange<NNamespace::ENamed1>::Min); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed1::N13, TEnumRange<NNamespace::ENamed1>::Max); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed2::N21, TEnumRange<NNamespace::ENamed2>::Min); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed2::N23, TEnumRange<NNamespace::ENamed2>::Max); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed3::N31, TEnumRange<NNamespace::ENamed3>::Min); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed3::N33, TEnumRange<NNamespace::ENamed3>::Max); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed4::N41, TEnumRange<NNamespace::ENamed4>::Min); + UNIT_ASSERT_VALUES_EQUAL(NNamespace::ENamed4::N43, TEnumRange<NNamespace::ENamed4>::Max); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<NNamespace::ENamed1>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<NNamespace::ENamed1>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(5, TEnumRange<NNamespace::ENamed2>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(9, TEnumRange<NNamespace::ENamed2>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<NNamespace::ENamed3>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<NNamespace::ENamed3>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<NNamespace::ENamed4>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<NNamespace::ENamed4>::UnderlyingMax); +} + +namespace { + enum class EAnon1 { + A11, + A12, + A13 + }; + Y_DEFINE_ENUM_MINMAX(EAnon1, A11, A13); + + enum EAnon2 { + A21 = 5, + A22, + A23 = 9 + }; + Y_DEFINE_ENUM_MINMAX(EAnon2, A21, A23); + + enum class EAnon3 { + A31, + A32, + A33 + }; + Y_DEFINE_ENUM_MAX(EAnon3, A33); + + enum EAnon4 { + A41, + A42, + A43 + }; + Y_DEFINE_ENUM_MAX(EAnon4, A43); +} + +void TEnumRangeTest::TestAnonNamespaceEnumRange() { + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<EAnon1>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<EAnon1>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(5, TEnumRange<EAnon2>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(9, TEnumRange<EAnon2>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<EAnon3>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<EAnon3>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<EAnon4>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<EAnon4>::UnderlyingMax); +} + +struct TTestStruct { + enum class EMember1 { + M11, + M12, + M13 + }; + Y_DEFINE_ENUM_MINMAX_FRIEND(EMember1, M11, M13); + + enum EMember2 { + M21 = 5, + M22, + M23 = 9 + }; + Y_DEFINE_ENUM_MINMAX_FRIEND(EMember2, M21, M23); + + enum class EMember3 { + M31, + M32, + M33 + }; + Y_DEFINE_ENUM_MAX_FRIEND(EMember3, M33); + + enum EMember4 { + M41, + M42, + M43 + }; + Y_DEFINE_ENUM_MAX_FRIEND(EMember4, M43); +}; + +Y_DEFINE_ENUM_SERIALIZATION(TTestStruct::EMember1, M1); +Y_DEFINE_ENUM_SERIALIZATION(TTestStruct::EMember2, M2); +Y_DEFINE_ENUM_SERIALIZATION(TTestStruct::EMember3, M3); +Y_DEFINE_ENUM_SERIALIZATION(TTestStruct::EMember4, M4); + +void TEnumRangeTest::TestMemberEnumRange() { + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember1::M11, TEnumRange<TTestStruct::EMember1>::Min); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember1::M13, TEnumRange<TTestStruct::EMember1>::Max); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember2::M21, TEnumRange<TTestStruct::EMember2>::Min); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember2::M23, TEnumRange<TTestStruct::EMember2>::Max); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember3::M31, TEnumRange<TTestStruct::EMember3>::Min); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember3::M33, TEnumRange<TTestStruct::EMember3>::Max); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember4::M41, TEnumRange<TTestStruct::EMember4>::Min); + UNIT_ASSERT_VALUES_EQUAL(TTestStruct::EMember4::M43, TEnumRange<TTestStruct::EMember4>::Max); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<TTestStruct::EMember1>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<TTestStruct::EMember1>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(5, TEnumRange<TTestStruct::EMember2>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(9, TEnumRange<TTestStruct::EMember2>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<TTestStruct::EMember3>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<TTestStruct::EMember3>::UnderlyingMax); + UNIT_ASSERT_VALUES_EQUAL(0, TEnumRange<TTestStruct::EMember4>::UnderlyingMin); + UNIT_ASSERT_VALUES_EQUAL(2, TEnumRange<TTestStruct::EMember4>::UnderlyingMax); +} diff --git a/util/generic/ut/ya.make b/util/generic/ut/ya.make index 35bdb72bd4..c4a968f877 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_range_ut.cpp generic/explicit_type_ut.cpp generic/flags_ut.cpp generic/function_ref_ut.cpp diff --git a/util/ya.make b/util/ya.make index 2d9d01c8da..5564e65eb0 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_range.cpp generic/explicit_type.cpp generic/fastqueue.cpp generic/flags.cpp |