views:

379

answers:

3

So I'm wondering about both setting and manipulating bit fields.

I've already found http://stackoverflow.com/questions/264552/cc-code-to-treat-a-character-array-as-a-bitstream which is similar to my question I guess but it doesn't doesn't give me a nice stl approach I am thinking has to exist.

I was thinking of bitsets from the stl but my data set is very complex with bit layouts of 1,2,3,4,7,8,16 bit formats.

Lets say I wanted to access an item in my data, the 4th field which is a 8 bit segment of code that crosses a byte boundry, is there an easy way to do this?

byte 1   byte 2
11112344 44444455

I'm looking for a good stl implementation to access the data in 4 or set the data in 4, I assume something exists for this as it seems silly to have to shift the data into a single byte and mask it off. writing it also seems difficult and like there should be an easier way to accomplish such a task.

Is there another way?

edit - my data set is around 20 bytes in length and i want to keep it all in bit order

+1  A: 

Can you explain why regular bitfields are insufficient? In other words why doesn't this:

    struct ComplexBitLayout {
      unsigned field1 :4;
      unsigned field2 :1;
      unsigned field3 :1;
      unsigned field4 :8;
      unsigned field5 :2;
   } __attribute__((__packed__)); // or your compiler's equivalent

   ComplexBitLayout cbl;

   cbl.field4 = x;

do what you want?

Is it that you want to be able to programmatically construct different layouts on the fly or something?

Tyler McHenry
It's tempting to use bitfields, but they are not portable, which can often be the dealbreaker.
Brian Neal
that is what I'm looking for, although as Brian said, not portable, and doesn't work with my OS
MacX.dmg
+1  A: 

The masking and shifting is going to happen regardless of any syntactic sugar you add. If you want to make things really easy to use, but a little harder to make in the first place, you can use a class, along with a bit of macro/template code to make it a little easier to define new classes:

template<bool> struct CompileTimeAssert;
template<> struct CompileTimeAssert<true> { };

#define ASSERT(check) if (!check) throw exception("Assertion Failure" #check)

#define ADDBITVALUE(backingField, backingFieldType, fieldName, offset, size) \
    public: \
    static const unsigned int fieldName##Offset = offset; \
    static const backingFieldType fieldName##Mask = CalculateMask<offset, size, backingFieldType>::Value; \
    public: \
    void Set##fieldName(backingFieldType value) \
    {\
     ASSERT(("Value too large for field.", (value & (fieldName##Mask >> fieldName##Offset)) == value));\
     backingField |= value << fieldName##Offset;\
    }\
    backingFieldType Get##fieldName() const\
    {\
     return (backingField & fieldName##Mask) >> fieldName##Offset;\
    }\
    private:

#define ADDSPANNEDVALUE(backingField1, backingField1Type, backingField2, backingField2Type, fieldName, offset1, size1, offset2, size2)\
    ADDBITVALUE(backingField1, backingField1Type, fieldName##internal1, offset1, size1)\
    ADDBITVALUE(backingField2, backingField2Type, fieldName##internal2, offset2, size2)\
    public: \
    void Set##fieldName(backingField1Type value) \
    {\
     backingField1Type value1 = value << (sizeof(backingField1Type)*8-size1);\
     value1 = value1 >> (sizeof(backingField1Type)*8-size1);\
     Set##fieldName##internal1(value1);\
     Set##fieldName##internal2(value >> size1);\
    }\
    backingField1Type Get##fieldName() const\
    {\
     return Get##fieldName##internal1() | (Get##fieldName##internal2() << size1);\
    }\
    private:

template <unsigned int Offset, int Size, typename T>
struct CalculateMask
{
    CompileTimeAssert<(Size > 0)> Object;
    static const T Value = (T)((1 << Offset) | CalculateMask<Offset + 1, Size - 1, T>::Value);
};

template <unsigned int Offset, typename T>
struct CalculateMask<Offset, 0, T>
{
    CompileTimeAssert<(Offset <= sizeof(T) * 8)> Object;
    static const T Value = 0;
};

Then define your class like this:

class BitGroup
{
    unsigned short Values;
    unsigned short Values2;
    ADDBITVALUE(Values, unsigned short, Field1, 0, 12);
    ADDSPANNEDVALUE(Values, unsigned short, Values2, unsigned short, Field2, 12, 4, 0, 2);
    ADDBITVALUE(Values2, unsigned short, Field3, 2, 14);
public:
    BitGroup() : Values(0), Values2(0) {}
};

Usage:

BitGroup bg;
bg.SetField1(15);
cout << bg.GetField1();
bg.SetField2(63);
cout << bg.GetField1();

You'll get a compile time assert if your fields fall outside of the range of the backing fields. There is no checking that fields don't overlap, so you'd have to watch out for that.

Eclipse
would that work for say 216 bits? it seems like it's a solution for 16 bits
MacX.dmg
You'd be stuck at 64 bits for the largest field.
Eclipse
What the hell's a "long short"? Isn't defining constant values what enums are for?
Jimmy J
It's just a typo. That's what I get for not compiling my first example. static consts are just as good as enums for constant values as enums.
Eclipse
A: 

It does appear that the existing classes such as vector<bool> or Boost.DynamicBitset aren't going to do anything for you.

The underlying implementation is going to have to do shifting and masking, nothing silly about that. Writing your own class or template using an underlying vector<bool> or vector<something_else> isn't that hard and then you get to optimize the shifting/masking code for what you think your requirements are, such as:

  • Is this random access or sequential? (If it's sequential then you can avoid some recomputation of your shifted-around masks.)
  • Are all elements the same size or are you indexing arbitrary-size elements at arbitrary bit-offsets?
Liudvikas Bukys