My question is already answered above. However, this has been bothering me forever, so I was able to leverage everyone's responses and came up with, what I think is for the first time, a mechanism that I really like!
This gives me a way to use C++ enumerations to store bit flag values, and then to logically bit-manipulate them at will, while never losing type information or the compiler's assistance with making sure that I fall into the pit of success. :)
I hope this helps you too!
(Note: this code compiles and runs properly under VS2013 update 1)
#pragma once
#include <type_traits>
// This is my ongoing attempt to make a really solid enumeration facility in C++11/14
//
// What I hate about C++98 (and older) enum
// - lack of namespace scoping of the non-class enum (name collisions everywhere)
// - auto conversion to numeric types (int i = MyEnumConstant), but no conversion back again (so supports losing info, but restricts regaining it)
//
// What I hate about C++11/14 built-in `enum class X`
// - having to constantly cast in order to treat them (now neither direction works)
// - no built-in mechanism to treat enumerated values as bits or bit-flags
template <typename enum_type>
class bitflag_enum
{
public:
// expose our underlying types
typedef enum_type enum_type;
typedef typename std::underlying_type<enum_type>::type store_type;
// constructors
bitflag_enum()
: m_bits(0)
{
}
bitflag_enum(enum_type flag)
: m_bits(static_cast<store_type>(flag))
{
}
explicit bitflag_enum(store_type value)
: m_bits(value)
{
}
// operators
operator bool() const
{
return m_bits != store_type(0);
}
// explicit accessors
store_type bits() const
{
return m_bits;
}
private:
store_type m_bits;
};
// because implicit conversion isn't considered if a type participates in template type deduction,
// we've defined both homogeneous and heterogeneous operators here for bitflag_enum<enum_type> and enum_type
// hence we define logical operators &, |, ^ and comparisons for TxT, TxU, UxT (where T is bitflag_enum<enum_type>, and U is enum_type)
template <typename enum_type>
bool operator != (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
return bitflag_enum<enum_type>(lhs.bits() != rhs.bits());
}
template <typename enum_type>
bool operator != (bitflag_enum<enum_type> lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(lhs.bits() != static_cast<store_type>(rhs));
}
template <typename enum_type>
bool operator != (enum_type lhs, bitflag_enum<enum_type> rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) != rhs.bits());
}
template <typename enum_type>
bool operator == (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
return bitflag_enum<enum_type>(lhs.bits() == rhs.bits());
}
template <typename enum_type>
bool operator == (bitflag_enum<enum_type> lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(lhs.bits() == static_cast<store_type>(rhs));
}
template <typename enum_type>
bool operator == (enum_type lhs, bitflag_enum<enum_type> rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) == rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator & (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
return bitflag_enum<enum_type>(lhs.bits() & rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator & (bitflag_enum<enum_type> lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(lhs.bits() & static_cast<store_type>(rhs));
}
template <typename enum_type>
bitflag_enum<enum_type> operator & (enum_type lhs, bitflag_enum<enum_type> rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs)& rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator | (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
return bitflag_enum<enum_type>(lhs.bits() | rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator | (bitflag_enum<enum_type> lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(lhs.bits() | static_cast<store_type>(rhs));
}
template <typename enum_type>
bitflag_enum<enum_type> operator | (enum_type lhs, bitflag_enum<enum_type> rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) | rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator ^ (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
return bitflag_enum<enum_type>(lhs.bits() ^ rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator ^ (bitflag_enum<enum_type> lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(lhs.bits() ^ static_cast<store_type>(rhs));
}
template <typename enum_type>
bitflag_enum<enum_type> operator ^ (enum_type lhs, bitflag_enum<enum_type> rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) ^ rhs.bits());
}
// The only missing pieces above are for the UxU cases
// we allow you to have those by defining a specialization of is_bitflag_enum<>, as follows:
//
// template <> struct is_bitflag_enum<YourEnumType> : std::true_type { };
//
// However, by default, no other types will convert to an bitflag_enum<> unless you explicitly say you want it
//
// If you have asked for them, then you can use MyEnum::ValueX | MyEnum::ValueY and that will produce a bitflag_enum<MyEnum>
// so your code can simply use your enumeration values with scope and as-if they were bit flags as you would think you could
// don't mess up existing enumerations or types by defining these global operators on every existing type!
template <typename enum_type> struct is_bitflag_enum : std::false_type { };
template <typename enum_type>
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type
operator & (enum_type lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) & static_cast<store_type>(rhs));
}
template <typename enum_type>
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type
operator | (enum_type lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) | static_cast<store_type>(rhs));
}
template <typename enum_type>
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type
operator ^ (enum_type lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) ^ static_cast<store_type>(rhs));
}