#pragma once #ifndef ENUM_INL_H_ #error "Direct inclusion of this file is not allowed, include enum.h" // For the sake of sane code completion. #include "enum.h" #endif #include #include #include #include namespace NYT { //////////////////////////////////////////////////////////////////////////////// #define ENUM__CLASS(name, underlyingType, seq) \ enum class name : underlyingType \ { \ PP_FOR_EACH(ENUM__DOMAIN_ITEM, seq) \ }; #define ENUM__DOMAIN_ITEM(item) \ PP_IF( \ PP_IS_SEQUENCE(item), \ ENUM__DOMAIN_ITEM_SEQ, \ ENUM__DOMAIN_ITEM_ATOMIC \ )(item)() #define ENUM__DOMAIN_ITEM_ATOMIC(item) \ item PP_COMMA #define ENUM__DOMAIN_ITEM_SEQ(seq) \ PP_ELEMENT(seq, 0) = PP_ELEMENT(seq, 1) PP_COMMA //////////////////////////////////////////////////////////////////////////////// namespace NDetail { template static constexpr bool AreValuesDistinct(const TValues& values) { for (int i = 0; i < static_cast(values.size()); ++i) { for (int j = i + 1; j < static_cast(values.size()); ++j) { if (values[i] == values[j]) { return false; } } } return true; } } // namespace NDetail //////////////////////////////////////////////////////////////////////////////// #define ENUM__BEGIN_TRAITS(name, underlyingType, isBit, isStringSerializable, seq) \ struct TEnumTraitsImpl_##name \ { \ using TType = name; \ using TUnderlying = underlyingType; \ [[maybe_unused]] static constexpr bool IsBitEnum = isBit; \ [[maybe_unused]] static constexpr bool IsStringSerializableEnum = isStringSerializable; \ [[maybe_unused]] static constexpr int DomainSize = PP_COUNT(seq); \ \ static constexpr std::array Names{{ \ PP_FOR_EACH(ENUM__GET_DOMAIN_NAMES_ITEM, seq) \ }}; \ static constexpr std::array Values{{ \ PP_FOR_EACH(ENUM__GET_DOMAIN_VALUES_ITEM, seq) \ }}; \ \ static TStringBuf GetTypeName() \ { \ static constexpr TStringBuf typeName = PP_STRINGIZE(name); \ return typeName; \ } \ \ static const TStringBuf* FindLiteralByValue(TType value) \ { \ for (int i = 0; i < DomainSize; ++i) { \ if (Values[i] == value) { \ return &Names[i]; \ } \ } \ return nullptr; \ } \ \ static bool FindValueByLiteral(TStringBuf literal, TType* result) \ { \ for (int i = 0; i < DomainSize; ++i) { \ if (Names[i] == literal) { \ *result = Values[i]; \ return true; \ } \ } \ return false; \ } \ \ static const std::array& GetDomainNames() \ { \ return Names; \ } \ \ static const std::array& GetDomainValues() \ { \ return Values; \ } \ \ static TType FromString(TStringBuf str) \ { \ TType value; \ if (!FindValueByLiteral(str, &value)) { \ throw ::NYT::TSimpleException(Sprintf("Error parsing %s value %s", \ PP_STRINGIZE(name), \ TString(str).Quote().c_str()).c_str()); \ } \ return value; \ } #define ENUM__GET_DOMAIN_VALUES_ITEM(item) \ PP_IF( \ PP_IS_SEQUENCE(item), \ ENUM__GET_DOMAIN_VALUES_ITEM_SEQ, \ ENUM__GET_DOMAIN_VALUES_ITEM_ATOMIC \ )(item) #define ENUM__GET_DOMAIN_VALUES_ITEM_SEQ(seq) \ ENUM__GET_DOMAIN_VALUES_ITEM_ATOMIC(PP_ELEMENT(seq, 0)) #define ENUM__GET_DOMAIN_VALUES_ITEM_ATOMIC(item) \ TType::item, #define ENUM__GET_DOMAIN_NAMES_ITEM(item) \ PP_IF( \ PP_IS_SEQUENCE(item), \ ENUM__GET_DOMAIN_NAMES_ITEM_SEQ, \ ENUM__GET_DOMAIN_NAMES_ITEM_ATOMIC \ )(item) #define ENUM__GET_DOMAIN_NAMES_ITEM_SEQ(seq) \ ENUM__GET_DOMAIN_NAMES_ITEM_ATOMIC(PP_ELEMENT(seq, 0)) #define ENUM__GET_DOMAIN_NAMES_ITEM_ATOMIC(item) \ TStringBuf(PP_STRINGIZE(item)), #define ENUM__DECOMPOSE \ static std::vector Decompose(TType value) \ { \ std::vector result; \ for (int i = 0; i < DomainSize; ++i) { \ if (static_cast(value) & static_cast(Values[i])) { \ result.push_back(Values[i]); \ } \ } \ return result; \ } #define ENUM__MINMAX \ static constexpr TType GetMinValue() \ { \ static_assert(!Values.empty()); \ return *std::min_element(std::begin(Values), std::end(Values)); \ } \ \ static constexpr TType GetMaxValue() \ { \ static_assert(!Values.empty()); \ return *std::max_element(std::begin(Values), std::end(Values)); \ } #define ENUM__VALIDATE_UNIQUE(name) \ static_assert(::NYT::NDetail::AreValuesDistinct(Values), \ "Enumeration " #name " contains duplicate values"); #define ENUM__END_TRAITS(name) \ }; \ \ [[maybe_unused]] inline TEnumTraitsImpl_##name GetEnumTraitsImpl(name) \ { \ return TEnumTraitsImpl_##name(); \ } \ \ using ::ToString; \ [[maybe_unused]] inline TString ToString(name value) \ { \ return ::NYT::TEnumTraits::ToString(value); \ } //////////////////////////////////////////////////////////////////////////////// template std::vector TEnumTraits::Decompose(T value) { return TImpl::Decompose(value); } template T TEnumTraits::FromString(TStringBuf str) { return TImpl::FromString(str); } template TString TEnumTraits::ToString(TType value) { TString result; const auto* literal = FindLiteralByValue(value); if (literal) { result = *literal; } else { result = GetTypeName(); result += "("; result += ::ToString(static_cast(value)); result += ")"; } return result; } template auto TEnumTraits::GetDomainValues() -> const std::array& { return TImpl::GetDomainValues(); } template auto TEnumTraits::GetDomainNames() -> const std::array& { return TImpl::GetDomainNames(); } template constexpr T TEnumTraits::GetMaxValue() { return TImpl::GetMaxValue(); } template constexpr T TEnumTraits::GetMinValue() { return TImpl::GetMinValue(); } template bool TEnumTraits::FindValueByLiteral(TStringBuf literal, TType* result) { return TImpl::FindValueByLiteral(literal, result); } template const TStringBuf* TEnumTraits::FindLiteralByValue(TType value) { return TImpl::FindLiteralByValue(value); } template TStringBuf TEnumTraits::GetTypeName() { return TImpl::GetTypeName(); } //////////////////////////////////////////////////////////////////////////////// template constexpr TEnumIndexedVector::TEnumIndexedVector() : Items_{} { } template constexpr TEnumIndexedVector::TEnumIndexedVector(std::initializer_list elements) : Items_{} { Y_ASSERT(std::distance(elements.begin(), elements.end()) <= N); size_t index = 0; for (const auto& element : elements) { Items_[index++] = element; } } template T& TEnumIndexedVector::operator[] (E index) { Y_ASSERT(index >= Min && index <= Max); return Items_[static_cast(index) - static_cast(Min)]; } template const T& TEnumIndexedVector::operator[] (E index) const { return const_cast(*this)[index]; } template T* TEnumIndexedVector::begin() { return Items_.data(); } template const T* TEnumIndexedVector::begin() const { return Items_.data(); } template T* TEnumIndexedVector::end() { return begin() + N; } template const T* TEnumIndexedVector::end() const { return begin() + N; } template bool TEnumIndexedVector::IsDomainValue(E value) { return value >= Min && value <= Max; } //////////////////////////////////////////////////////////////////////////////// #define ENUM__BINARY_BITWISE_OPERATOR(T, assignOp, op) \ [[maybe_unused]] inline constexpr T operator op (T lhs, T rhs) \ { \ using TUnderlying = typename TEnumTraits::TUnderlying; \ return T(static_cast(lhs) op static_cast(rhs)); \ } \ \ [[maybe_unused]] inline T& operator assignOp (T& lhs, T rhs) \ { \ using TUnderlying = typename TEnumTraits::TUnderlying; \ lhs = T(static_cast(lhs) op static_cast(rhs)); \ return lhs; \ } #define ENUM__UNARY_BITWISE_OPERATOR(T, op) \ [[maybe_unused]] inline constexpr T operator op (T value) \ { \ using TUnderlying = typename TEnumTraits::TUnderlying; \ return T(op static_cast(value)); \ } #define ENUM__BIT_SHIFT_OPERATOR(T, assignOp, op) \ [[maybe_unused]] inline constexpr T operator op (T lhs, size_t rhs) \ { \ using TUnderlying = typename TEnumTraits::TUnderlying; \ return T(static_cast(lhs) op rhs); \ } \ \ [[maybe_unused]] inline T& operator assignOp (T& lhs, size_t rhs) \ { \ using TUnderlying = typename TEnumTraits::TUnderlying; \ lhs = T(static_cast(lhs) op rhs); \ return lhs; \ } #define ENUM__BITWISE_OPS(name) \ ENUM__BINARY_BITWISE_OPERATOR(name, &=, &) \ ENUM__BINARY_BITWISE_OPERATOR(name, |=, | ) \ ENUM__BINARY_BITWISE_OPERATOR(name, ^=, ^) \ ENUM__UNARY_BITWISE_OPERATOR(name, ~) \ ENUM__BIT_SHIFT_OPERATOR(name, <<=, << ) \ ENUM__BIT_SHIFT_OPERATOR(name, >>=, >> ) //////////////////////////////////////////////////////////////////////////////// template bool Any(E value) { return static_cast::TUnderlying>(value) != 0; } template bool None(E value) { return static_cast::TUnderlying>(value) == 0; } //////////////////////////////////////////////////////////////////////////////// } // namespace NYT