#pragma once
#include <util/generic/fwd.h>
#include <util/generic/vector.h>
#include <util/generic/map.h>
#include <cstddef>
#include <type_traits>
/*
A file with declarations of enumeration-related functions.
It doesn't contains definitions. To generate them you have to add
GENERATE_ENUM_SERIALIZATION_WITH_HEADER(your_header_with_your_enum.h)
or
GENERATE_ENUM_SERIALIZATION(your_header_with_your_enum.h)
in your ya.make
@see https://st.yandex-team.ru/IGNIETFERRO-333
@see https://wiki.yandex-team.ru/PoiskovajaPlatforma/Build/WritingCmakefiles/#generate-enum-with-header
*/
/**
* Returns number of distinct items in enum or enum class
*
* @tparam EnumT enum type
*/
template <typename EnumT>
Y_CONST_FUNCTION constexpr size_t GetEnumItemsCount();
namespace NEnumSerializationRuntime {
namespace NDetail {
template <typename EEnum>
struct TSelectEnumRepresentationType;
template <typename TEnumType, typename TRepresentationType, class TStorage = TVector<TRepresentationType>>
class TMappedArrayView;
template <typename TEnumType, typename TRepresentationType, typename TValueType, class TStorage = TMap<TRepresentationType, TValueType>>
class TMappedDictView;
} // namespace NDetail
/// Class with behaviour similar to TMap<EnumT, TValueType>
template <typename EnumT, typename TValueType>
using TMappedDictView = NDetail::TMappedDictView<EnumT, typename NDetail::TSelectEnumRepresentationType<EnumT>::TType, TValueType>;
/// Class with behaviour similar to TVector<EnumT>
template <typename EnumT>
using TMappedArrayView = NDetail::TMappedArrayView<EnumT, typename NDetail::TSelectEnumRepresentationType<EnumT>::TType>;
/**
* Returns names for items in enum or enum class
*
* @tparam EnumT enum type
*/
template <typename EnumT>
TMappedDictView<EnumT, TString> GetEnumNamesImpl();
/**
* Returns unique items in enum or enum class
*
* @tparam EnumT enum type
*/
template <typename EnumT>
::NEnumSerializationRuntime::TMappedArrayView<EnumT> GetEnumAllValuesImpl();
/**
* Returns human-readable comma-separated list of names in enum or enum class
*
* @tparam EnumT enum type
*/
template <typename EnumT>
const TString& GetEnumAllNamesImpl();
/**
* Returns C++ identifiers for items in enum or enum class
*
* @tparam EnumT enum type
*/
template <typename EnumT>
const TVector<TString>& GetEnumAllCppNamesImpl();
/**
* Converts @c e to a string. Works like @c ToString(e) function, but returns @c TStringBuf instead of @c TString.
* Thus works slightly faster and usually avoids any dynamic memory allocation.
* @throw yexception is case of unknown enum value
*/
template <typename EnumT>
TStringBuf ToStringBuf(EnumT e);
} // namespace NEnumSerializationRuntime
/**
* Returns names for items in enum or enum class
*
* @tparam EnumT enum type
*/
template <typename EnumT>
Y_CONST_FUNCTION ::NEnumSerializationRuntime::TMappedDictView<EnumT, TString> GetEnumNames() {
return ::NEnumSerializationRuntime::GetEnumNamesImpl<EnumT>();
}
/**
* Returns unique items in enum or enum class
*
* @tparam EnumT enum type
*/
template <typename EnumT>
Y_CONST_FUNCTION ::NEnumSerializationRuntime::TMappedArrayView<EnumT> GetEnumAllValues() {
return ::NEnumSerializationRuntime::GetEnumAllValuesImpl<EnumT>();
}
/**
* Returns human-readable comma-separated list of names in enum or enum class
*
* @tparam EnumT enum type
*/
template <typename EnumT>
Y_CONST_FUNCTION const TString& GetEnumAllNames() {
return ::NEnumSerializationRuntime::GetEnumAllNamesImpl<EnumT>();
}
/**
* Returns C++ identifiers for items in enum or enum class
*
* @tparam EnumT enum type
*/
template <typename EnumT>
Y_CONST_FUNCTION const TVector<TString>& GetEnumAllCppNames() {
return ::NEnumSerializationRuntime::GetEnumAllCppNamesImpl<EnumT>();
}
namespace NEnumSerializationRuntime {
namespace NDetail {
/// Checks that the `From` type can be promoted up to the `To` type without losses
template <typename From, typename To>
struct TIsPromotable: public std::is_same<std::common_type_t<From, To>, To> {
static_assert(std::is_integral<From>::value, "`From` type has to be an integer");
static_assert(std::is_integral<To>::value, "`To` type has to be an integer");
};
/// Selects enum representation type. Works like std::underlying_type_t<>, but promotes small types up to `int`
template <typename EEnum>
struct TSelectEnumRepresentationType {
using TUnderlyingType = std::underlying_type_t<EEnum>;
using TIsSigned = std::is_signed<TUnderlyingType>;
using TRepresentationType = std::conditional_t<
TIsSigned::value,
std::conditional_t<
TIsPromotable<TUnderlyingType, int>::value,
int,
long long>,
std::conditional_t<
TIsPromotable<TUnderlyingType, unsigned>::value,
unsigned,
unsigned long long>>;
using TType = TRepresentationType;
static_assert(sizeof(TUnderlyingType) <= sizeof(TType), "size of `TType` is not smaller than the size of `TUnderlyingType`");
};
template <typename TEnumType, typename TRepresentationType>
class TMappedViewBase {
static_assert(sizeof(std::underlying_type_t<TEnumType>) <= sizeof(TRepresentationType), "Internal type is probably too small to represent all possible values");
public:
static constexpr TEnumType CastFromRepresentationType(const TRepresentationType key) noexcept {
return static_cast<TEnumType>(key);
}
static constexpr TRepresentationType CastToRepresentationType(const TEnumType key) noexcept {
return static_cast<TRepresentationType>(key);
}
};
/// Wrapper class with behaviour similar to TVector<EnumT>
///
/// @tparam TEnumType enum type at the external interface
/// @tparam TRepresentationType designated underlying type of enum
/// @tparam TStorage internal container type
template <typename TEnumType, typename TRepresentationType, class TStorage>
class TMappedArrayView: public TMappedViewBase<TEnumType, TRepresentationType> {
public:
using value_type = TEnumType;
public:
TMappedArrayView(const TStorage& a) noexcept
: Ref(a)
{
}
class TIterator {
public:
using TSlaveIteratorType = typename TStorage::const_iterator;
using difference_type = std::ptrdiff_t;
using value_type = TEnumType;
using pointer = const TEnumType*;
using reference = const TEnumType&;
using iterator_category = std::bidirectional_iterator_tag;
public:
TIterator(TSlaveIteratorType it)
: Slave(std::move(it))
{
}
bool operator==(const TIterator& it) const {
return Slave == it.Slave;
}
bool operator!=(const TIterator& it) const {
return !(*this == it);
}
TEnumType operator*() const {
return TMappedArrayView::CastFromRepresentationType(*Slave);
}
TIterator& operator++() {
++Slave;
return *this;
}
TIterator& operator--() {
--Slave;
return *this;
}
TIterator operator++(int) {
auto temp = Slave;
++Slave;
return temp;
}
TIterator operator--(int) {
auto temp = Slave;
--Slave;
return temp;
}
private:
TSlaveIteratorType Slave;
};
TIterator begin() const {
return Ref.begin();
}
TIterator end() const {
return Ref.end();
}
size_t size() const {
return Ref.size();
}
Y_PURE_FUNCTION bool empty() const {
return Ref.empty();
}
TEnumType at(size_t index) const {
return this->CastFromRepresentationType(Ref.at(index));
}
TEnumType operator[](size_t index) const {
return this->CastFromRepresentationType(Ref[index]);
}
// Allocate container and copy view's content into it
template <template <class...> class TContainer = TVector>
TContainer<TEnumType> Materialize() const {
return {begin(), end()};
}
private:
const TStorage& Ref;
};
/// Wrapper class with behaviour similar to TMap<EnumT, TValueType>
///
/// @tparam TEnumType enum type at the external interface
/// @tparam TRepresentationType designated underlying type of enum
/// @tparam TValueType mapped value
/// @tparam TStorage internal container type
template <typename TEnumType, typename TRepresentationType, typename TValueType, class TStorage>
class TMappedDictView: public TMappedViewBase<TEnumType, TRepresentationType> {
public:
using TMappedItemType = std::pair<const TEnumType, const TValueType&>;
class TDereferenceResultHolder {
public:
TDereferenceResultHolder(const TRepresentationType enumValue, const TValueType& payload) noexcept
: Data(TMappedDictView::CastFromRepresentationType(enumValue), payload)
{
}
const TMappedItemType* operator->() const noexcept {
return &Data;
}
private:
TMappedItemType Data;
};
TMappedDictView(const TStorage& m) noexcept
: Ref(m)
{
}
class TIterator {
public:
using TSlaveIteratorType = typename TStorage::const_iterator;
using difference_type = std::ptrdiff_t;
using value_type = TMappedItemType;
using pointer = const TMappedItemType*;
using reference = const TMappedItemType&;
using iterator_category = std::bidirectional_iterator_tag;
public:
TIterator(TSlaveIteratorType it)
: Slave(std::move(it))
{
}
bool operator==(const TIterator& it) const {
return Slave == it.Slave;
}
bool operator!=(const TIterator& it) const {
return !(*this == it);
}
TDereferenceResultHolder operator->() const {
return {Slave->first, Slave->second};
}
TMappedItemType operator*() const {
return {TMappedDictView::CastFromRepresentationType(Slave->first), Slave->second};
}
TIterator& operator++() {
++Slave;
return *this;
}
TIterator& operator--() {
--Slave;
return *this;
}
TIterator operator++(int) {
auto temp = Slave;
++Slave;
return temp;
}
TIterator operator--(int) {
auto temp = Slave;
--Slave;
return temp;
}
private:
TSlaveIteratorType Slave;
};
TIterator begin() const {
return Ref.begin();
}
TIterator end() const {
return Ref.end();
}
size_t size() const {
return Ref.size();
}
Y_PURE_FUNCTION bool empty() const {
return Ref.empty();
}
bool contains(const TEnumType key) const {
return Ref.contains(this->CastToRepresentationType(key));
}
TIterator find(const TEnumType key) const {
return Ref.find(this->CastToRepresentationType(key));
}
const TValueType& at(const TEnumType key) const {
return Ref.at(this->CastToRepresentationType(key));
}
// Allocate container and copy view's content into it
template <template <class...> class TContainer = TMap>
TContainer<TEnumType, TValueType> Materialize() const {
return {begin(), end()};
}
private:
const TStorage& Ref;
};
} // namespace NDetail
} // namespace NEnumSerializationRuntime