TLDR: Provide a "bitmask" class with a constexpr constructor that creates the bitmask based on the input parameter (applying a bitshift) in the compile-time, which could be used instead of a function-like MACRO.
I'm trying to implement an event manager that stores events as bit flags. This would be a wrapper for an RTOS feature such as event flag group
I would like to have the events specified this way
enum Event
{
FOO,
BAR,
BAZ,
};
Not this way:
// This construct is harder to maintain and easier to make a mistake
enum Event
{
FOO = 0b0001,
BAR = 0b0010,
BAZ = 0b0100,
};
The events would be then used in this way:
class EventService
{
uint32_t current_events;
public:
void set_event(const EventMask ev)
{
current_events |= ev.mask;
}
bool check_event(const EventMask ev)
{
const bool was_set = current_events & ev.mask != 0;
current_events &= ~(ev.mask);
return was_set;
}
};
EventService s;
void event_producer()
{
s.set_event(Event::FOO);
}
void event_handler()
{
if(s.check_event(Event::FOO))
{
// do something;
}
if(s.check_event(Event::BAR))
{
// do something else;
}
if(s.check_event(Event::BAZ))
{
// do something else;
}
}
Therefore I've implemented a helper class EventMask
with constexpr constructor that just makes a bit shift and stores it as a const member.
struct EventMask
{
const uint32_t mask;
template<typename Enum>
constexpr EventMask(Enum event_id) : mask(1u << event_id)
{
static_assert(std::is_enum<Enum>::value, "Enum required.");
constexpr_assert(event_id <= MAX_ID);
}
};
But unfortunately, these bitmask objects are not created in the build-time while using check_event
and set_event
. Here is the whole example put together.
set_event(Event::FOO);
check_event(Event::BAZ); //this should fail in the conpile time because I set BAZ to be higher then MAX_ID
I know I could have a MACRO wrapper around set_event
and check_event
but I believe there is a more modern way (probably I just don't understand the constexpr in the first place.) On the other hand, I could have a template with a template argument N
and body 1u << N
but this would make the code bloat, wouldn't it?
I've been thinking about consteval
but this will not allow to send a runtime event (like stored somewhere as a configuration) which appears as a valid usecase
Not really an answer why your assertion doesn't work but I avoid the problem with:
enum class Bits
{
FOO,
BAR,
BAZ,
};
enum class Event
{
FOO = 1llu << int(Bits::FOO),
BAR = 1llu << int(Bits::BAR),
BAZ = 1llu << int(Bits::BAZ),
};
Event operator |(const Event &lhs, const Event &rhs) {
return Event(int(lhs) | int(rhs));
}
Event x = Event::FOO | Event::BAR;
Lots of boilerplate but it works.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.