aboutsummaryrefslogblamecommitdiffstats
path: root/util/generic/serialized_enum.h
blob: 8188c43eede29936850512d1f6e46e39ff369f54 (plain) (tree)
1
2
3
4
5
6
7
8
            
                             
                                
 
                  
                      

  
                                                                 
                                                                         
                                                             
 
               










                                                                                                       
                                                      
 
















































                                                                                                                                                






                                                                                                                      
 




                                                
                                                                                              







                                                                  
                                                                                          







                                                                             
                                                   







                                                                     
                                                               





























































































































                                                                                                                                                                            
                                                
























































































































                                                                                                                 
                                                


                                                      
                                                                         



















                                                                     
#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;
    }

    /// 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);
}

/**
 * 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;
        };
    }
}