10

I need to extact bytes from the bitset which may (not) contain a multiple of CHAR_BIT bits. I now how many of the bits in the bitset I need to put into an array. For example,

the bits set is declared as std::bitset < 40> id;

There is a separate variable nBits how many of the bits in id are usable. Now I want to extract those bits in multiples of CHAR_BIT. I also need to take care of cases where nBits % CHAR_BIT != 0. I am okay to put this into an array of uint8

dubnde
  • 4,359
  • 9
  • 43
  • 63
  • 2
    Harsh... if you had less than sizeof(unsigned long) bits, this would be straightforward using `bitset::to_ulong`. As it is, I don't think there is a simple solution. `std::bitset` doesn't have something like `data()` as `std::vector` does (though the gcc version has an undocumented and experimental `_M_getdata` function which is just that...)`. Since there is no other thing, you can only access the individual bits separately. Or, serialize to a string or go via a stream, but neither of these is particularly efficient. – Damon Nov 28 '11 at 15:24

3 Answers3

21

You can use boost::dynamic_bitset, which can be converted to a range of "blocks" using boost::to_block_range.

#include <cstdlib>
#include <cstdint>
#include <iterator>
#include <vector>
#include <boost/dynamic_bitset.hpp>

int main()
{
    typedef uint8_t Block; // Make the block size one byte
    typedef boost::dynamic_bitset<Block> Bitset;

    Bitset bitset(40); // 40 bits

    // Assign random bits
    for (int i=0; i<40; ++i)
    {
        bitset[i] = std::rand() % 2;
    }

    // Copy bytes to buffer
    std::vector<Block> bytes;
    boost::to_block_range(bitset, std::back_inserter(bytes));
}
Emile Cormier
  • 28,391
  • 15
  • 94
  • 122
  • One gotcha to watch out for is that the inner type of the vector needs to match the block type that was used to construct the bitset (in this case, `unsigned char`). If the bitset has 4-byte blocks but the vector has 1-byte blocks, `to_block` will silently chop off the latter 3 bytes of each block. So, it's somewhat safer to change the vector declaration in Emile's answer to `std::vector bytes`. – prideout Sep 28 '15 at 17:40
  • Correction to my comment: the inner type is `Bitset::block_type`, not `Bitset::Block`. – prideout Sep 28 '15 at 17:55
  • @prideout: Added `Block` typedef to avoid the situation you describe. – Emile Cormier Sep 29 '15 at 00:21
3

With standard C++11, you can get the bytes out of your 40-bit bitset with shifting and masking. I didn't deal with handling different values rather than 8 and 40 and handling when the second number is not a multiple of the first.

#include <bitset>
#include <iostream>
#include <cstdint>

int main() {
    constexpr int numBits = 40;

    std::bitset<numBits> foo(0x1234567890);
    std::bitset<numBits> mask(0xff);

    for (int i = 0; i < numBits / 8; ++i) {
        auto byte =
            static_cast<uint8_t>(((foo >> (8 * i)) & mask).to_ulong());
        std::cout << std::hex << setfill('0') << setw(2) << static_cast<int>(byte) << std::endl;
    }
}
Jim Hunziker
  • 14,111
  • 8
  • 58
  • 64
3

Unfortunately there's no good way within the language, assuming you need for than the number of bits in an unsigned long (in which case you could use to_ulong). You'll have to iterate over all the bits and generate the array of bytes yourself.

Mark B
  • 95,107
  • 10
  • 109
  • 188