Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to initialize bitfields with a C++ Constructor?

Tags:

c++

bit-fields

First off, I’m not concerned with portability, and can safely assume that the endianness will not change. Assuming I read a hardware register value, I would like to overlay that register value over bitfields so that I can refer to the individual fields in the register without using bit masks.

EDIT: Fixed problems pointed out by GMan, and adjusted the code so it's clearer for future readers.

SEE: Anders K. & Michael J's answers below for a more eloquent solution.

#include <iostream>

/// \class HardwareRegister
/// Abstracts out bitfields in a hardware register.
/// \warning  This is non-portable code.
class HardwareRegister
{
   public:
      /// Constructor.
      /// \param[in]  registerValue - the value of the entire register. The
      ///                             value will be overlayed onto the bitfields
      ///                             defined in this class.
      HardwareRegister(unsigned long registerValue = 0)
      {
         /// Lots of casting to get registerValue to overlay on top of the
         /// bitfields
         *this = *(reinterpret_cast<HardwareRegister*>(&registerValue));
      }


      /// Bitfields of this register.
      /// The data type of this field should be the same size as the register
      /// unsigned short for 16 bit register
      /// unsigned long for 32 bit register.
      ///
      /// \warning Remember endianess! Order of the following bitfields are
      ///          important.
      ///          Big Endian    - Start with the most signifcant bits first.
      ///          Little Endian - Start with the least signifcant bits first.
      unsigned long field1: 8;
      unsigned long field2:16;
      unsigned long field3: 8;
}; //end class Hardware


int main()
{

   unsigned long registerValue = 0xFFFFFF00;
   HardwareRegister  testRegister(registerValue);

   // Prints out for little endianess machine
   // Field 1 = 0
   // Field 2 = 65535
   // Field 3 = 255
   std::cout << "Field 1 = " << testRegister.field1 << std::endl;
   std::cout << "Field 2 = " << testRegister.field2 << std::endl;
   std::cout << "Field 3 = " << testRegister.field3 << std::endl;
}
like image 534
Dennis Miller Avatar asked Sep 08 '26 09:09

Dennis Miller


2 Answers

Bitfields don't work that way. You can't assign a scalar value to a struct full of bitfields. It looks like you already know this since you used reinterpret_cast, but since reinterpret_cast isn't guaranteed to do very much, it's just rolling the dice.

You need to encode and decode the values if you want to translate between bitfield structs and scalars.

    HW_Register(unsigned char value)
      : field1( value & 3 ),
        field2( value >> 2 & 3 ),
        field3( value >> 4 & 7 )
        {}

Edit: The reason you don't get any output is that the ASCII characters corresponding to the numbers in the fields are non-printing. Try this:

std::cout << "Field 1 = " << (int) testRegister.field1 << std::endl;
std::cout << "Field 2 = " << (int) testRegister.field2 << std::endl;
std::cout << "Field 3 = " << (int) testRegister.field3 << std::endl;
like image 124
Potatoswatter Avatar answered Sep 09 '26 22:09

Potatoswatter


don't do this

 *this = *(reinterpret_cast<HW_Register*>(&registerValue));

the 'this' pointer shouldn't be fiddled with in that way:

HW_Register reg(val)
HW_Register *reg = new HW_Register(val)

here 'this' is in two different places in memory

instead have an internal union/struct to hold the value, that way its easy to convert back and forth (since you are not interested in portability)

e.g.

union
{
   struct {
     unsigned short field1:2;
     unsigned short field2:4;
     unsigned short field3:2;
    ...
   } bits;
   unsigned short value;
} reg

edit: true enough with the name 'register'

like image 43
AndersK Avatar answered Sep 09 '26 23:09

AndersK



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!