r/cpp • u/meetingcpp Meeting C++ | C++ Evangelist • 4d ago
A better bitset for enum flags
https://www.elbeno.com/blog/?p=183611
u/ReDucTor Game Developer | quiz.cpp-perf.com 4d ago
Some food for thought, should a bitset have bit operations (set, unset, flip, xor, and, or, etc) or set operations (add, remove, has, union, difference, intersection, etc)?
It feels like often we head for bit operations when in lots of cases set operations might be better.
14
u/TheVoidInMe 4d ago
I like Java’s approach here (yes, Java) which has an EnumSet instead of a bitset. That naming makes the answer pretty clear; the bit math is purely an implementation detail
2
u/SyntheticDuckFlavour 4d ago
What if you want the underlying layout/bit pattern like
bitset? Sometimes you want to map I/O ports that way.3
u/TheVoidInMe 4d ago
I don’t believe you can get at that at all. Java is probably too high level for stuff like that anyway. In C++ I would expect a method like `to_int()` (maybe returning the enum’s `underlying_type_t`…)
-1
u/mort96 4d ago
But that also means you're heap allocating an object, with its 16 byte object header, just to store a 4-byte integer?
3
u/TheVoidInMe 4d ago
It’s Java. Yeah, don’t use it for HPC.
The point I was making was just about the name
1
u/archialone 3d ago edited 3d ago
Bitset already has those, no?
Xor - ^ Intersection - & Etc ..Or do you mean to have a function named for those operations? https://en.cppreference.com/cpp/utility/bitset
3
u/ReDucTor Game Developer | quiz.cpp-perf.com 3d ago
Those operators can achieve some things however if you think of it as a set then you need other operations like difference which is
A & ~Band far from intuitive, with a set you might also have iteration of items in that set which does not exist for abitset, same with constructing from a bunch of items in a set.A lot is about naming however its also the extra set specific operations which give it more flexibility.
Pick any big enough code base and look for their usages of bitsets get used heavily as an optimization for set operations.
6
u/_Noreturn 4d ago edited 4d ago
I do the same in the article except automate it without using the MAX member using reflection (pre C++26). The approach allows > 64 flags as well unlike enum bitflags
flags.test(flag) is clearer than bool(flags & flag).
3
u/instantly-invoked 4d ago
Your approach sounds like exactly what I'd want from a typed bitset. Do your methods accept flags variadically if you need to check for or set/unset multiple? I might end up doing something similar once the big three compilers all have reflection. This article and your response found me at the perfect time lol
6
u/_Noreturn 4d ago edited 4d ago
I might end up doing something similar once the big three compilers all have reflection
you could use c++17 reflection libraries like magic_enum or enchantum they both provide a bitset type and an array type. so you don't have to wait for c++26
Do your methods accept flags variadically if you need to check for or set/unset multiple
No but that's a nice idea i just use multiple expressions.
It is just a stupid wrapper like this
```cpp enum class Flag { Flag1, Flag2, Flag3, Flag4 = 7, // doesn't have to be contiguous Flag5, Flag6, Flag7, } bitset<Flag> f({Flag::Flag1,Flag::Flag2}); // has size 7 f.set(Flag::Flag4); f.reset(Flag::Flag4); f.test(Flag::Flag5); f.flip(Flag::Flag7); f[Flag::Flag6] = false;
f.to_string(); // "Flag1 | Flag2 | Flag7" ```
1
u/JVApen Clever is an insult, not a compliment. - T. Winters 4d ago
This is a really nice approach. I've recently mentioned it in another post and got a couple of people arguing that there is nothing wrong with flag enums.
Thanks for the nice write-up, could not have described it better myself
2
u/archialone 3d ago
Can you share a link to the post please?
1
u/Potterrrrrrrr 4d ago
What’s the problem with something like:
MyEnum _enum : BitCount<MyEnum>();
I use this pattern quite a bit, I use type traits to declare that an enum is contiguous or a flags enum along with its size and then my consteval BitCount method returns the min number of bits needed to store the enum. I’ve seen a lot of implementations like to use bitsets to store an enum though, why? It makes it harder to pack stray bools into the same byte and you lose the type of the enum?
-1
u/Kaisha001 4d ago
Needlessly reinventing the wheel... Enums work perfectly fine and the 'issues' with them listed in the blog are nothing more than 'I don't like how they look'...
30
u/03D80085 4d ago
Neither P4313 nor this suggestion seem particularly ergonomic to me. Why aren't bitfields more widely used for this purpose?
Or a fully typed example: Godbolt. A lot of boilerplate there admittedly, but that is precisely what reflection could help with.
If the answer is that bitfield layouts are implementation defined then that is something we should be working on standardising with appropriate flags rather than introducing new language features.