How can the following MakeMask
function be implemented?
Say I have a scoped enum that represents a set of bitflags. I want to create a consteval function that returns a bitmask of that enum. This bitmask represents all the bits present in the given enum.
We can get the enum type using std::underlying_type
, but we can just assume uint32_t
for the discussion.
enum class Flags : uint32_t {
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
X = 1 << 7,
};
template<typename T>
consteval uint32_t MakeMask() {
// ...
}
// M is a bitmask with elements A, B, C, and X enabled.
constexpr uint32_t M = MakeMask<Flags>();
// M is like: 0b0100_0111
How can the following MakeMask
function be implemented?
Say I have a scoped enum that represents a set of bitflags. I want to create a consteval function that returns a bitmask of that enum. This bitmask represents all the bits present in the given enum.
We can get the enum type using std::underlying_type
, but we can just assume uint32_t
for the discussion.
enum class Flags : uint32_t {
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
X = 1 << 7,
};
template<typename T>
consteval uint32_t MakeMask() {
// ...
}
// M is a bitmask with elements A, B, C, and X enabled.
constexpr uint32_t M = MakeMask<Flags>();
// M is like: 0b0100_0111
Share
Improve this question
edited Jan 30 at 16:00
Dess
asked Jan 30 at 15:53
DessDess
2,67927 silver badges41 bronze badges
6
|
Show 1 more comment
2 Answers
Reset to default 0An alternative solution would be to have your mask within your enum itself.
enum class Flags : uint32_t {
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
X = 1 << 7,
MASK = A + B + C + X
};
It's much simpler than many think. You just need good old <bitset>
. But I would blend some C++20 spice in first:
#include <concepts>
#include <type_traits>
template<typename E>
concept flags_enumeration =
( std::is_enum_v<T>
& requires{
{ std::to_underlying(E::last_flag) }
->std::unsigned_integral;
requires (std::to_underlying(E::last_flag) > 0);
requires (std::to_underlying(E::last_flag) < 64 );
};
Now I can decorate std::bitset
:
#include <bitset>
template<flags_enumeration FE>
struct flagset{
std::bitset<std::to_underlying(E::last_flag)> value;
constexpr auto& set(std::same_as<FE> ...fe){
if (sizeof...(fe))
(void)(value.set(std::to_underlying(fe)), ...);
else
value.set());
return *this;
};//set
constexpr auto& reset(std::same_as<FE> ...fe){
if (sizeof...(fe))
(void)(value.reset(std::to_underlying(fe)), ...);
else
value.reset());
return *this;
};//reset
constexpr auto& flip(std::same_as<FE> ...fe){
if (sizeof...(fe))
(void)(value.flip(std::to_underlying(fe)), ...);
else
value.flip());
return *this;
};//flip
constexpr bool test(std::same_as<FE> ...fe) const {
return (sizeof...(fe))
? (value.test(std::to_underlying(fe)) and ...);
: value.all();
};//test
};//flagset
The above snippet defines flagset
as an aggregate, because I want to avoid the wrapping the full set of operations available on `std::bitset'. But it is possible to provide a more complete class.
Its Usage would be very simple:
enum class my_flags
: std::uint16_t{
flag1,
flag2,
flag3,
last_flag = flag3
};//enum class my flags;
flagset<my_flags> flg;
flg.set(my_flags::flag1,my_flags::flag3);
flagset<my_flags> flg2;
flg2.value |= flg.value;
auto intflags = flg2.value.to_ulonglong();
Note that no member of the enumeration needs an enumerator value; last_flag
is the only exception, and marks the end of flags. Each flag is eventually an automatically generated bit index.
The flags_enumeration
concept defines valid enumerations as being unsigned and having a none-zero last_flag
below the bitdepth of unsigned long long
returned by the bitset::to_ulonglong
method. A better design would use <limits>
instead of magic number 64, but that would complicate the example snippet here.
mask = (1 << 18) - 1
to the end of the enum, and you're done! Or bettermask [[maybe_unused]] = (1 << 18) - 1
. – HolyBlackCat Commented Jan 30 at 16:01