-
Notifications
You must be signed in to change notification settings - Fork 241
Bitwise operation helpers
WIL defines a number of helper macros in wil/common.h
to improve readability
and reduce the error rate of bitwise operations.
The macros improve readability by expressing the operation concisely: Flags and variables are specified only once per macro invocation, and the macro name describes the nature of the operation.
The macros reduce errors by capturing complex bit operations into a single step. Single-flag operations validate at compile time that exactly one flag is specified.
For example,
// instead of: days = days & ~DaysOfWeek::Monday;
WI_ClearFlag(days, DaysOfWeek::Monday);
// instead of: if ((days & (DaysOfWeek::Monday | DaysOfWeek::Tuesday)) == (DaysOfWeek::Monday | DaysOfWeek::Tuesday))
if (WI_AreAllFlagsSet(days, DaysOfWeek::Monday | DaysOfWeek::Tuesday))
In the following table:
-
var
is a variable. -
flag
is a compile-time constant that is a power of two (a single flag). This requirement is enforced at compile time. -
flags
,mask
, andnewFlags
are a run-time value or a compile-time constant. It can consist of zero or more flags. -
cond
is a boolean condition expression.
The macro modifies the value in var
as indicated.
Macro | Operation |
---|---|
WI_SetFlag(var, flag); |
Set a single bit. |
WI_SetFlagIf(var, flag, cond); |
Set a single bit if cond is true . |
WI_SetAllFlags(var, flags); |
Set zero or more bits. |
WI_ClearFlag(var, flag); |
Clear a single bit. |
WI_ClearFlagIf(var, flag, cond); |
Clear a single bit if cond is true . |
WI_ClearAllFlags(var, flags); |
Clear zero or more bits. |
WI_UpdateFlag(var, flag, cond); |
Set a single bit if cond is true, or clear the bit if cond is false. |
WI_UpdateFlagsInMask(var, mask, newFlags); |
Change the flags specified by mask to match the corresponding bits in newFlags . |
WI_ToggleFlag(var, flag); |
Toggle (XOR) a single bit. |
WI_ToggleAllFlags(var, flags); |
Toggle (XOR) zero or more bits. |
In the follow table:
-
val
is an expression. -
flag
is a compile-time constant that is a power of two (a single flag). This requirement is enforced at compile time. -
flags
,mask
, andnewflags
are a run-time value or a compile-time constant. It can consist of zero or more flags.
The macro tests the value val
as indicated, returning true
if the condition is met or `false if the condition is not met.
Macro | Condition |
---|---|
WI_IsFlagSet(val, flag) |
The bit specified by flag is set in val . |
WI_IsAnyFlagSet(val, flags) |
Any bit specified by flags is set in val . |
WI_AreAllFlagsSet(val, flags) |
All bits specified by flags are set in val . |
WI_IsFlagClear(val, flag) |
The bit specified by flag is not set in val . |
WI_IsAnyFlagClear(val, flags) |
Any bit specified by flags is not set in val . |
WI_AreAllFlagsClear(val, flags) |
No bits specified by flags are set in val . |
WI_IsSingleFlagSet(val) |
Exactly one bit is set in val . |
WI_IsSingleFlagSetInMask(val, mask) |
Exactly one bit specified by mask is set in val . |
WI_IsClearOrSingleFlagSet(val) |
At most one bit is set in val . |
WI_IsClearOrSingleFlagSetInMask(val, mask) |
At most one bit specified by mask is set in val . |
enum class DaysOfWeek
{
None = 0x0000,
Sunday = 0x0001,
Monday = 0x0002,
Tuesday = 0x0004,
Wednesday = 0x0008,
Thursday = 0x0010,
Friday = 0x0020,
Saturday = 0x0040,
};
DEFINE_ENUM_FLAG_OPERATORS(DaysOfWeek);
extern DaysOfWeek GetAvailableDays();
DaysOfWeek days = DaysOfWeek::Sunday;
WI_SetFlag(days, DaysOfWeek::Saturday);
// result: days = Sunday | Saturday
WI_ClearFlag(days, DaysOfWeek::Sunday);
// result: days = Saturday
WI_SetAllFlags(days, DaysOfWeek::Monday | DaysOfWeek::Tuesday);
// result: days = Monday | Tuesday | Saturday
WI_SetFlagIf(days, DaysOfWeek::Friday, false);
// result: days = Monday | Tuesdy | Saturday (unchanged)
WI_ToggleFlag(days, DaysOfWeek::Monday);
// result: days = Tuesday | Saturday
WI_UpdateFlag(days, DaysOfWeek::Monday, false);
// result: days = Tuesday | Saturday
// Sets Monday and clears Tuesday. Wednesday is not affected.
WI_UpdateFlagsInMask(days, DaysOfWeek::Monday | DaysOfWeek::Tuesday,
DaysOfWeek::Monday | DaysOfWeek::Wednesday);
// result: days = Monday | Saturday
if (WI_IsFlagSet(days, DaysOfWeek::Sunday)) ... // false
if (WI_AreAllFlagsSet(days, GetAvailableDays()) ...
if (WI_AreAllFlagsClear(days, GetAvailableDays()) ...
// Test whether exactly one flag is set.
if (WI_IsSingleFlagSet(days)) ... // false
The following uses will not compile.
WI_SetFlag(days, DaysOfWeek::None); // error
// Use WI_SetAllFlags instead.
WI_SetFlag(days, DaysOfWeek::Thursday | DaysOfWeek::Friday); // error
// Use WI_SetAllFlags instead.
WI_SetFlag(days, GetAvailableDays()); // error
if (WI_IsFlagSet(days, DaysOfWeek::None)) // error
// Error.
// Use WI_AreAllFlagsSet or WI_IsAnyFlagSet depending on what you actually want.
if (WI_IsFlagSet(days, DaysOfWeek::Sunday | DaysOfWeek::Saturday)) // error
The WI_EnumValue
macro returns an unsigned integer of the same width and numeric value
as the provided enum value.
This is handy when validating that an enum's fields match some other set of integers.
enum class DropEffects
{
None = 0x0000,
Copy = 0x0001,
Move = 0x0002,
Link = 0x0004,
};
static_assert(WI_EnumValue(DropEffects::None) == DROPEFFECT_NONE);
static_assert(WI_EnumValue(DropEffects::Copy) == DROPEFFECT_COPY);
static_assert(WI_EnumValue(DropEffects::Move) == DROPEFFECT_MOVE);
static_assert(WI_EnumValue(DropEffects::Link) == DROPEFFECT_LINK);
Forces a compiler error if the value is not an exact power of two. Returns the original value. This is typically used to build other bit manipulation macros.
if (effect & WI_StaticAssertSingleBitSet(DropEffects::Copy))
{
...
}