Gunther Piez
Gunther Piez

Reputation: 30449

Variable-sized bitfields with aliasing

I have some struct containig a bitfield, which may vary in size. Example:

struct BitfieldSmallBase {
    uint8_t a:2;
    uint8_t b:3;
    ....
}

struct BitfieldLargeBase {
    uint8_t a:4;
    uint8_t b:5;
    ....
}

and a union to access all bits at once:

template<typename T>
union Bitfield 
{
    T bits;
    uint8_t all;    // <-------------  Here is the problem

    bool operator & (Bitfield<T> x) const {
        return !!(all & x.all);
    }
    Bitfield<T> operator + (Bitfield<T> x) const {
        Bitfield<T> temp;
        temp.all = all + x.all;   //works, because I can assume no overflow will happen
        return temp;
    }
    ....
}

typedef Bitfield<BitfieldSmallBase> BitfieldSmall;
typedef Bitfield<BitfieldLargeBase> BitfieldLarge;

The problem is: For some bitfield base classes, an uint8_t is not sufficient. BitfieldSmall does fit into a uint8_t, but BitfieldLarge does not. The data needs to be packed as tightly as possible (it will be handled by SSE instructions later), so always using uint16_t is out of question. Is there a way to declare the "all" field with an integral type, whose size is the same as the bitfield? Or another way to access bits as a whole?

I can of course forego the use of the template and declare every kind of bitfield explicitly, but I would like to avoid code repetition (there is quite a list of operators und member functions).

Upvotes: 1

Views: 2350

Answers (6)

Pavel Minaev
Pavel Minaev

Reputation: 101665

How about this?

#include <limits.h>

template <class T>
union BitField
{
    T bits;
    unsigned all : sizeof(T) * CHAR_BIT;
};

Upvotes: 0

Matt
Matt

Reputation:

Make the number of bytes you need part of the template parameters:

template <typename T, int S=1>
struct BitField 
{
   union
   {
      T bits;
      unsigned char bytes[S];
   };
};

typedef Bitfield<BitfieldSmallBase, 1>  BitfieldSmall;
typedef Bitfield<BitfieldLargeBase, 2> BitfieldLarge;

Upvotes: 0

D.Shawley
D.Shawley

Reputation: 59623

You might want to consider std::bitset or boost::dynamic_bitset rather than rolling your own. In any case, steer clear of std::vector<bool>!

Upvotes: 1

catwalk
catwalk

Reputation: 6476

you can use template metaprogramming to define a template function that maps from BitfieldSmallBase, BitfieldLargeBase, etc into another type - uint8_t by default and to uint16_t for BitfieldLargeBase as a template specialization and then use that like this:

union Bitfield 
{
    T bits;
    typename F<T>::holder_type all;
};

Upvotes: 1

Will
Will

Reputation: 75673

I've learnt the hard way that whilst the bit width on vars that you're using is a convenient way of getting the compiler to do your masking and shifting for you, you cannot make assumptions about the order and padding of the members in the struct. Its compiler dependent and the compiler really does change the order and such dependent upon the other code in your project.

If you want to treat a byte as discrete fields, you really have to do it the hard way.

Upvotes: 1

John Kugelman
John Kugelman

Reputation: 362037

You could make the integral type a template parameter as well.

template<typename T, typename U>
union Bitfield 
{
    T bits;
    U all;
}

typedef Bitfield<BitfieldSmallBase, uint8_t>  BitfieldSmall;
typedef Bitfield<BitfieldLargeBase, uint16_t> BitfieldLarge;

Upvotes: 5

Related Questions