I made a tool set to facilitate bit manipulation as follows. Any comments are welcome, be it about the interface design, the implementation, or even the naming. Also, I want to make sure that the tool set is not lacking something for use-cases I failed to catch :-) Original code and unit tests can be found here and here.
/// The index is zero-based, starting from the least significant bit.
/// The most significant bit or sign bit is the head bit.
template <typename T>
constexpr unsigned num_bits(T = 0) {
return sizeof(T) << 3;
}
template <typename T>
constexpr unsigned head_bit_idx(T = 0) {
return num_bits<T>() - 1;
}
template <typename T>
T bit_flag(unsigned idx) {
// since C++14, `1 << (num_bits<int> - 1)` is well-defined and makes `INT_MIN`
return static_cast<T>(1) << idx;
}
template <typename T>
constexpr T head_bit_flag() {
return static_cast<T>(1) << head_bit_idx<T>();
}
template <typename T>
bool test_bit(T x, unsigned idx) {
return x & bit_flag<T>(idx);
}
template <typename T>
bool test_head_bit(T x) {
return test_bit(x, head_bit_idx(x));
}
template <typename T>
T set_bit(T x, unsigned idx) {
return x | bit_flag<T>(idx);
}
template <typename T>
T set_head_bit(T x) {
return set_bit(x, head_bit_idx<T>());
}
template <typename T>
T clear_bit(T x, unsigned idx) {
return x & ~bit_flag<T>(idx);
}
template <typename T>
T clear_head_bit(T x) {
return clear_bit(x, head_bit_idx<T>());
}
template <typename T>
T flip_bit(T x, unsigned idx) {
return x ^ bit_flag<T>(idx);
}
template <typename T>
T flip_head_bit(T x) {
return flip_bit(x, head_bit_idx<T>());
}
template <typename T>
unsigned num_bits_set(T x) {
unsigned cnt = 0;
// behavior of signed type underflow is unspecified
std::make_unsigned_t<T> v = x;
while (v) {
++cnt;
v &= v - 1;
}
return cnt;
}
std::bitset? \$\endgroup\$std::bitsetis a little heavy and inconvenient for me. \$\endgroup\$