Okay, so we're at C++ 17 and there still isn't a satisfactory answer to a really great bitflags interface in C++.
We have enum
which bleed their member values into the enclosing scope, but do implicitly convert to their underlying type, so can be used as-if they were bit flags but refuse to reassign back into the enum without casting.
We have enum class
which solves the name scope issue so that their values must be explicitly named MyEnum::MyFlag
or even MyClass::MyEnum::MyFlag
, but they do not implicitly convert to their underlying type, so cannot be used as bit-flags without endless casting back and forth.
And finally, we have the old bit-fields from C
such as:
struct FileFlags {
unsigned ReadOnly : 1;
unsigned Hidden : 1;
...
};
Which has the drawback of having no good way to initialize itself as a whole - one has to resort to using memset or casting the address or similar to overwrite the entire value or initialize it all at once or otherwise manipulate multiple bits at once. It also suffers from not being able to name the value of a given flag, as opposed to its address - so there is no name representing 0x02, whereas there is such a name when using enums, hence it's easy with enums to name a combination of flags, such as FileFlags::ReadOnly | FileFlags::Hidden
- there simply isn't a good way to say as much for bit-fields.
In addition we still have simple constexpr
or #define
to name bit values and then simply not use enums at all. This works, but completely dissociates the bit values from the underlying bitflag type. Perhaps this is ultimately not the worst approach, particularly if the bit flag values are constexpr
within a struct to give them their own name-scope?
struct FileFlags {
constexpr static uint16_t ReadOnly = 0x01u;
constexpr static uint16_t Hidden = 0x02u;
...
}
So, as it currently stands, we have a lot of techniques, none of which add up to a really solid way to say
Here is a type which has the following valid bit-flags in it, it has its own name-scope, and these bits and type should be freely usable with standard bitwise operators such as | & ^ ~, and they should be comparable to integral values such as 0, and the result of any bitwise operators should remain the named type, and not devolve into an integral
All of that said, there are a number of attempts floating around to try to produce the above entity in C++ -
- The windows OS team developed a simple macro that generates C++ code to define the necessary missing operators on a given enum type
DEFINE_ENUM_FLAG_OPERATORS(EnumType)
which then defines operator | & ^ ~ and the associated assignment ops such as |= and etc. - 'grisumbras' has a public GIT project for enabling bitflag semantics with scoped enums here, which uses
enable_if
meta programming to allow a given enum to convert to a bit-flag type which supports the missing operators and back again silently. - Without knowing the above, I wrote a relatively simple bit_flags wrapper which defines all of the bitwise operators on itself, so that one can use a
bit_flags<EnumType> flags
and thenflags
has bitwise semantics. What this fails to do is allow the enumerated base to actually properly handle bitwise operators directly, so you cannot sayEnumType::ReadOnly | EnumType::Hidden
even when using abit_flags<EnumType>
because the underlying enum itself still doesn't support the necessary operators. I had to end up doing the same thing essentially as #1 and #2 above, and enablingoperator | (EnumType, EnumType)
for the various bitwise operators by requiring users to declare a specialization for a meta type for their enum such astemplate <> struct is_bitflag_enum<EnumType> : std::true_type {};
Ultimately, the problem with #1, #2, and #3 is that it is not possible (as far as I know) to define the missing operators on the enum itself (as in #1) or to define the necessary enabler type (e.g. template <> struct is_bitflag_enum<EnumType> : std::true_type {};
as in #2 and partially #3) at class scope. Those must happen outside of a class or struct, as C++ simply doesn't have a mechanism that I am aware of which would allow me to make such declarations within a class.
So now, I have the desire to have a set of flags that should be scoped to a given class, but I cannot use those flags within the class header (e.g. default initialization, inline functions, etc.) because I cannot enable any of the machinery that allows the enum to be treated as bitflags until after the closing brace for the class definition. Or, I can define all such flag-enums outside of the class that they belong to, so that I can then invoke the "make this enum into a bitwise type" before the user-class definition, to have full use of that functionality in the client class - but now the bit flags are in the outer scope instead of being associated to the class itself.
This isn't the end of the world - none of the above is. But all of it causes endless headaches when writing my code - and stops me from writing it in the most natural way - i.e. with a given flag-enum that belongs to a specific class within (scoped to) that client class, but with bitwise flag-semantics (my approach #3 almost allows this - so long as everything is wrapped by a bit_flags - to explicitly enable the needed bitwise compatibility).
All of this still leaves me with the annoying sense that this could be much better than it is!
There surely should be - and perhaps is but I haven't figured it out yet - approach to enums to enable bitwise operators on them while allowing them to be declared and used within an enclosing class scope...
Does anyone have a wip or an approach I haven't considered above, that would allow me "the best of all possible worlds" on this?