Reputation: 30885
I have hard time to understand why to use Bitwise operations in enum's and then in the code . why not using just numbers or boolean's for example :
enum
{
RS_BLEND = (1 << 0),
RS_BLEND_FUNC = (1 << 1),
RS_CULL_FACE = (1 << 2),
RS_DEPTH_TEST = (1 << 3),
RS_DEPTH_WRITE = (1 << 4),
RS_DEPTH_FUNC = (1 << 5),
RS_CULL_FACE_SIDE = (1 << 6),
// RS_STENCIL_TEST = (1 << 7),
// RS_STENCIL_WRITE = (1 << 8),
// RS_STENCIL_FUNC = (1 << 9),
// RS_STENCIL_OP = (1 << 10),
RS_FRONT_FACE = (1 << 11),
RS_ALL_ONES = 0xFFFFFFFF,
};
void RenderState::StateBlock::setCullFace(bool enabled)
{
_cullFaceEnabled = enabled;
if (!enabled)
{
_bits &= ~RS_CULL_FACE;
}
else
{
_bits |= RS_CULL_FACE;
}
}
this is not critical or embedded software .
Upvotes: 2
Views: 159
Reputation:
1 bit vs. 8 bits if you use booleans instead. Using 1/8th the memory is a pretty amazing savings to represent the exact same data (and using 8 times more just because someone is uncomfortable with bitwise operations is likewise incredibly wasteful), and that can matter if you're dealing with hot data frequently accessed in a loop (I only have 64KB per core of L1 cache on my i7, e.g., not to mention limited registers).
Further, if you use bits here, you can test for two or more bits being set with a single instruction. You can also efficiently find a free bit or a set bit among 64 bits using FFZ/FFS all at once. You can easily invert, say, 64 bits at a time instead of looping through 64 booleans and having to do that one at a time which is not only incredibly efficient for the CPU, but also less efficient and productive for the developer to write out all that code when he could just use one operator.
The benefits go on and on. I actually think more people should be using bitwise operations, not less. It takes some time to get used to thinking in terms of bits, but it doesn't take too long.
Upvotes: 0
Reputation: 36597
It is convenience.
In the sample code, there are clearly (including what's in comments) at least 12 distinct settings that are each on or off, but that can be on or off in combination.
Bit fiddling allows turning groups of individual settings (including groups of one) on or off, or testing if particular groups are on or off without fuss.
Numeric values, and numeric operations, can do that, but not as conveniently.
For example, it is easier (and less error prone) to test if at least one of the CULL_FACE and DEPTH_FACE settings are on by using
if (flags & (RS_CULL_FACE | RS_DEPTH_FACE))
than it is keep track of all the possible numeric values for which one those two settings may be true, and to test against each one.
Upvotes: 0
Reputation: 3184
These enum values are what's known as flags used in a bitfield (here, _bits
). Similar behaviour could be obtained by declaring a struct of booleans:
struct field {
bool rs_blend;
bool rs_blend_func;
...
};
However, such a struct will require at least one byte per entry and will be awkward to handle, so the developer took a different approach by encoding these values into the bits of an integral value.
Any integer of value 1 has the "right-most" (least significant) bit set, so 1 << i
has exactly the i
th bit equal to 1, which means that every constant in that enum (RS_BLEND
, RS_BLEND_FUNC
) encodes one bit. This is actually a rather common idiom, especially in C where handling structs is far more verbose.
By using bitwise operations, etc may be set or cleared at once in this bitfield. For example RS_BLEND | RS_BLEND_FUNC
creates a bitfield that has exactly these two flags set. See this thread for details about the usage of bitwise operations in this context.
Upvotes: 1
Reputation: 114461
The advantage of using bits instead of booleans is that you can manipulate directly sets of values. For example defining:
const int FILLED = (1 << 0);
const int STROKED = (1 << 1);
const int SHADOW = (1 << 2);
const int BLINK = (1 << 3);
you can have a function accepting a draw_mode
parameter and call it like
draw_symbol(FILLED | SHADOW | BLINK, "X");
i.e. passing a subset of the values directly.
Using a container instead of a single integer parameter would require more code to write and to read. It would also be less efficient but in some cases this is not the most important point.
Upvotes: 4