Reputation: 106549
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
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; }
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
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
Reputation: 21773
If you don't care about performance, change your options to set<FindFilesOptions>
!
Upvotes: 0
Reputation: 153840
It is certainly possible to use enum class
es 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 class
es 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
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