Billy ONeal
Billy ONeal

Reputation: 106549

How does one use an enum class as a set of flags?

Let's say I have a set of flags and a class like this:

/// <summary>Options controlling a search for files.</summary>
enum class FindFilesOptions : unsigned char
{
    LocalSearch = 0,
    RecursiveSearch = 1,
    IncludeDotDirectories = 2
};

class FindFiles : boost::noncopyable
{
    /* omitted */
public:
    FindFiles(std::wstring const& pattern, FindFilesOptions options);
    /* omitted */
}

and I want a caller to be able to select more than one option:

FindFiles handle(Append(basicRootPath, L"*"),
    FindFilesOptions::RecursiveSearch | FindFilesOptions::IncludeDotDirectories);

Is it possible to support this in a strongly-typed way with C++11 enum class, or do I have to revert to untyped enumerations?

(I know the caller could static_cast to the underlying type and static_cast back, but I don't want the caller to have to do that)

Upvotes: 13

Views: 5217

Answers (5)

Potatoswatter
Potatoswatter

Reputation: 137810

Templates play well with enum class so you can define sets of operators that work on sets of similar enumeration types. The key is to use a traits template to specify what interface(s) each enumeration conforms/subscribes to.

As a start:

enum class mood_flag {
    jumpy,
    happy,
    upset,
    count // size of enumeration
};

template<>
struct enum_traits< mood_flag > {
    static constexpr bool bit_index = true;
};

template< typename t >
struct flag_bits : std::bitset< static_cast< int >( t::count ) > {
    flag_bits( t bit ) // implicit
        { this->set( static_cast< int >( bit ) ); }

    // Should be explicit but I'm lazy to type:
    flag_bits( typename flag_bits::bitset set )
        : flag_bits::bitset( set ) {}
};

template< typename e >
typename std::enable_if< enum_traits< e >::bit_index,
    flag_bits< e > >::type
operator | ( flag_bits< e > set, e next )
    { return set | flag_bits< e >( next ); }

template< typename e >
typename std::enable_if< enum_traits< e >::bit_index,
    flag_bits< e > >::type
operator | ( e first, e next )
    { return flag_bits< e >( first ) | next; }

http://ideone.com/kJ271Z

GCC 4.9 reported that some implicit member functions were constexpr while I was getting this to compile, so the templates should probably be so as well.

This should probably also have a free function to_scalar or something which returns an unsigned integer type given either an individual flag or a flag_bits set.

Upvotes: 5

Natok
Natok

Reputation: 131

How about defining FindFiles so that it takes std::initializer_list of FindFilesOptions.

void FindFiles(std::wstring const& pattern, std::initializer_list<FindFilesOptions> options)
{
  auto has_option = [&](FindFilesOptions const option)
  {
    return std::find(std::begin(options), std::end(options), option) != std::end(options);
  };

  if (has_option(FindFilesOptions::LocalSearch))
  {
    // ...
  }

  if (has_option(FindFilesOptions::RecursiveSearch))
  {
    // ...
  }

  if (has_option(FindFilesOptions::IncludeDotDirectories))
  {
    // ...
  }
}

Then you could call it like so:

FindFiles({}, {FindFilesOptions::RecursiveSearch, FindFilesOptions::IncludeDotDirectories});

Upvotes: 1

Neil Kirk
Neil Kirk

Reputation: 21773

If you don't care about performance, change your options to set<FindFilesOptions>!

Upvotes: 0

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153840

It is certainly possible to use enum classes for bitmaps. It is, unfortunately, a bit painful to do so: You need to define the necessary bit operations on your type. Below is an example how this could look like. It would be nice if the enum classes could derive from some other type which could live in a suitable namespace defining the necessary operator boilerplate code.

#include <iostream>
#include <type_traits>

enum class bitmap: unsigned char
{
    a = 0x01,
    b = 0x02,
    c = 0x04
};

bitmap operator& (bitmap x, bitmap y)
{
    typedef std::underlying_type<bitmap>::type uchar;
    return bitmap(uchar(x) & uchar(y));
}

bitmap operator| (bitmap x, bitmap y)
{
    typedef std::underlying_type<bitmap>::type uchar;
    return bitmap(uchar(x) | uchar(y));
}

bitmap operator^ (bitmap x, bitmap y)
{
    typedef std::underlying_type<bitmap>::type uchar;
    return bitmap(uchar(x) ^ uchar(y));
}

bool test(bitmap x)
{
    return std::underlying_type<bitmap>::type(x);
}

int main()
{
    bitmap v = bitmap::a | bitmap::b;
    if (test(v & bitmap::a)) {
        std::cout << "a ";
    }
    if (test(v & bitmap::b)) {
        std::cout << "b ";
    }
    if (test(v & bitmap::c)) {
        std::cout << "c ";
    }
    std::cout << '\n';
}

Upvotes: 14

galop1n
galop1n

Reputation: 8824

The problem is not the explicit enum type but the class scope.

With C++11, enum as compile time constant loose a lot of interest compared to a bunch of constexpr when you need operate on the value ( bitwise operation, incrementation, etc)

Upvotes: 0

Related Questions