8

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?

Kornel Kisielewicz
  • 55,802
  • 15
  • 111
  • 149
  • I'm a little unsure what you're asking; any chance you could add some details about what you'd like to do, or possibly an example? – Nate Kohl Jun 27 '13 at 18:27
  • From what I get, you want separate-but-similar type to `std::bitset` with type-safe (no enum-to-int) flag access by enumerations. Is my understanding correct? – milleniumbug Jun 27 '13 at 18:31
  • Exactly. Basically I have existing `enum class` definitions and I want to pack them into a type-safe `bitset`. – Kornel Kisielewicz Jun 27 '13 at 18:43

1 Answers1

10

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.

milleniumbug
  • 15,379
  • 3
  • 47
  • 71
  • Hmm, that should be possible to make into a general template, right? – Kornel Kisielewicz Jun 27 '13 at 19:09
  • @KornelKisielewicz Yes, this code can be templated quite easily. Just didn't write with templates directly, because I'm not quite comfortable with them yet :). – milleniumbug Jun 27 '13 at 19:11
  • Ok, I see a potential problem - there's no easy way to find the max value of an enum (without resorting to adding another enum value)... so it would probably have to be a second parameter of the template, what wouldn't be too safe :/. – Kornel Kisielewicz Jun 27 '13 at 19:14