Search code examples
c++c++11enum-classstd-bitset

Using enum class with std::bitset


First of all I want a normal enumeration instead of a bit-based enumeration, because the amount of different enums will be beyond any integral type. I also want to take advantage of the type safety of C++11 enum class. To do so, the natural choice would be std::bitset, however I have no idea how to bind those two together.

A custom bitset would be needed? How to go around the implementation of such a class?


Solution

  • Since enum classes are wrappers for enums, you can cast them to underlying type. And using some private inheritance you can selectively import some functionalities from C++ stdlib classes without worrying about Liskov's principle. Composition resulted in clearer code. Using these functionalities, we can wrap std::bitset. Following code contains only subset of functionalites, but it can be expanded further.

    There's a problem with max value - that you can't get maximum value of enum class (or am I wrong?). So I added EnumTraits. Now users are required to specialize EnumTraits with const value max equal to the max value of enum before class can be used.

    #include <bitset>
    #include <type_traits>
    
    template<typename T>
    struct EnumTraits;
    
    template<typename T>
    class EnumClassBitset
    {
    private:
        std::bitset<static_cast<typename std::underlying_type<T>::type>(EnumTraits<T>::max)> c;
    
        typename std::underlying_type<T>::type get_value(T v) const
        {
            return static_cast<typename std::underlying_type<T>::type>(v);
        }
    
    public:
        EnumClassBitset() : c()
        {
    
        }
    
        bool test(T pos) const
        {
            return c.test(get_value(pos));
        }
    
        EnumClassBitset& reset(T pos)
        {
            c.reset(get_value(pos));
            return *this;
        }
    
        EnumClassBitset& flip(T pos)
        {
            c.flip(get_value(pos));
            return *this;
        }
    };
    
    enum class BitFlags
    {
        False,
        True, 
        FileNotFound,
        Write,
        Read,
        MaxVal
    };
    
    template<>
    struct EnumTraits<BitFlags>
    {
        static const BitFlags max = BitFlags::MaxVal;
    };
    
    #include <iostream>
    
    int main()
    {
        EnumClassBitset<BitFlags> f;
        f.flip(BitFlags::True);
        f.flip(BitFlags::FileNotFound);
        //f.flip(2); //fails to compile
        std::cout << "Is False? " << f.test(BitFlags::False) << "\n";
        std::cout << "Is True? " << f.test(BitFlags::True) << "\n";
        std::cout << "Is FileNotFound? " << f.test(BitFlags::FileNotFound) << "\n";
        std::cout << "Is Write? " << f.test(BitFlags::Write) << "\n";
        std::cout << "Is Read? " << f.test(BitFlags::Read) << "\n";
    }
    

    Since enums don't have much functionality unfortunately, and what is more, C++11 with enum classes don't improve the situation, some programmers use static map wrapped in a class. Definitely a good read.