Introduction
I've been developing software using C/C++ for a very long time yet I still have to think twice when I encounter
bitwise operations, let alone when I have to explain them to somebody else! For example:
unsigned const STAT_ONE = 0x0001;
unsigned const STAT_TWO = 0x0002;
unsigned status = 0x0001;
if(status & (STAT_ONE | STAT_TWO))
DoSomething();
We all know that the preceding statement checks if both STAT_ONE
and STAT_TWO
bits are
set then DoSomething()
will be executed... or is it if either STAT_ONE
or STAT_TWO
bits are set? Lets see:
0001 or
0010
----
0011 and
0001
----
0001
Because the result is non-zero then DoSomething()
will be executed. I think this can be very tricky,
especially if the expressions get too complex. Isn't the intent of the following code easier to understand and less
error-prone?
unsigned const STAT_ONE = 0x0001;
unsigned const STAT_TWO = 0x0002;
unsigned status = 0x0001;
if(isAnyBitSet(status, STAT_ONE | STAT_TWO))
DoSomething();
if(areAllBitsSet(status, STAT_ONE | STAT_TWO))
DoSomething();
Also it's very convenient to access a bit's value by its position like this:
if(isBitSetByPos(status, 5))
bitClearByPos(status, 5);
All the functions provided in the BitTools.h header are inlined so there is no size or run-time speed
tradeoffs to worry about.
Templates
Bitmask-based functions
template <class T, class U>
bool isAnyBitSet(T value, U mask)
Returns true
if any of the bits in mask
is set in value
. Defined as:
(value & mask) != 0
1010 and
0110
----
0010
template <class T, class U>
bool areAllBitsSet(T value, U mask)
Returns true
if all the bits in mask
are set in value
. Defined as:
(value & mask) == mask
1010 and
1110
----
1010
template <class T, class U>
bool areAllBitsClear(T value, U mask)
Returns true
if all the bits in mask
are cleared in value
. Defined as:
(value & mask) == 0
1010 and
0101
----
0000
template <class T, class U>
T setBits(T value, U mask)
Returns value
with the mask
bits set. Defined as: value | mask
1000 or
0110
----
1110
template <class T, class U>
T setBitsExcept(T value, U mask)
Returns value
with the all the bits set except the mask
bits. Defined as:
value | ~mask
1001 not
----
0110 or
0001
----
0111
template <class T, class U>
T clearBits(T value, U mask)
Returns value
with the mask
bits cleared. Defined as: value & ~mask
1001 not
----
0110 and
1111
----
0110
template <class T, class U>
T clearBitsExcept(T value, U mask)
Returns value
with the all the bits cleared except the mask
bits. Defined as:
value & mask
0010 and
0110
----
0010
template <class T, class U>
T setClearBits(T value, U add, U remove)
Returns value
with the add
bits set and the remove
bits cleared.
Defined as: (value | add) & ~remove
1101 or
0101
----
0111
0001 not
----
1110 and
0111
----
0110
template <class T, class U, class V>
T setBits(T value, U mask, V set)
Returns value
with the mask
bits set or cleared depending on the value of
set
.
Position-based functions
template <class T>
T setBitByPos(T value, unsigned char n)
Returns value
with the n
th bit set. Defined as value | (1 << n)
template <class T>
T clearBitByPos(T value, unsigned char n)
Returns value
with the n
th bit cleared. Defined as value & ~(1 << n)
template <class T>
bool isBitSetByPos(T value, unsigned char n)
Returns true
if value
has the the n
th bit set. Defined as
(value & (1 << n)) != 0
template <class T>
bool isBitClearByPos(T value, unsigned char n)
Returns true
if value
has the the n
th bit cleared. Defined as
(value & (1 << n)) == 0
Conclusion
The templates contained in BitTools.h provide an easier and less error-prone way of expressing bitwise
operations with no performance or size penalties compared to hand written code. I hope you find these functions
as useful and easy to use as I did.