On Tue, Nov 18, 2025, 10:56 Ruben Perez via Boost <boost@lists.boost.org> wrote:
Hi all,
I've recently found myself frustrated due to the amount of boilerplate that bitmask types require. Say, for example, asio::cancellation_type_t: https://www.boost.org/doc/libs/latest/boost/asio/cancellation_type.hpp
When using enum class to avoid pouring identifiers into the parent namespace, you need to re-implement all the bitwise operators. This is not a lot of work, but is empty boilerplate, and implies adding tests and docs.
Reflection may aid with this. I've written a small proof of concept: https://access_refl.compiler-explorer.com/z/jdaxf9q54
Pasting here the basics, just in case:
namespace bitmasks {
struct bitmask_t {}; inline constexpr bitmask_t bitmask {};
// Checks that t has our annotation consteval bool has_bitmask_annotation(std::meta::info t) { ... }
template <class E> concept BitmaskType = std::is_scoped_enum_v<E> && has_bitmask_annotation(^^E);
}
// Intentionally in the global namespace template <bitmasks::BitmaskType E> E operator|(E e1, E e2) { using U = std::underlying_type_t<E>; return static_cast<E>(static_cast<U>(e1) | static_cast<U>(e2)); }
The idea is that you use the [[=bitmask]] annotation on your enum, and you get all the operators for free. I could also add optional pretty-printing.
Questions:
* Do you think this is a problem worth solving, or am I being too picky? * Do we already have something similar in Boost? I've found a BOOST_BITMASK macro (https://www.boost.org/doc/libs/latest/boost/detail/bitmask.hpp), but it doesn't seem to be public. * Do you think the approach shown above is sound? In particular, operators are in the global namespace so that they work with any enum in any namespace correctly annotated. * Do you think such a small component could be useful in Boost at some point?
Thanks, Ruben. _______________________________________________ Boost mailing list -- boost@lists.boost.org To unsubscribe send an email to boost-leave@lists.boost.org https://lists.boost.org/mailman3/lists/boost.lists.boost.org/ Archived at: https://lists.boost.org/archives/list/boost@lists.boost.org/message/XES3FGMG...
I have always considered this a self-inflicted problem. Saying that an `enum class E { A=1, B=2, C=4 };` can have a value of `E{(int)E::A | (int)E::B}` is a category error. Of course, an object of type E _can_ have this representation, but `enum` is meant to express a full set of valid values. This helps readers understand code and compilers warn about unhandled enum values. Bit masks have been done effortlessly in C since forever. Just use a normal `enum E { A=1, B=2, C=4 };` and create flag sets with `unsigned flags = A | B;`. Trying to shoehorn this into enum classes with macros and reflection (!) is absurd to me, to be honest. If you really want a more modern solution that also makes sense in the type system, I have used a utility class template like this before. ``` enum class E { A, B, C }; flag_set<E> flags{E::A, E::B}; flags.add(E::C); flags.erase(E::A); bool b = flags.contains(E::B); ``` `flag_set<E>` can internally shift enum values and apply them to the underlying integer. Best regards, Janko