How to initialize bitfields with a C++ Constructor?

别来无恙 提交于 2020-07-09 03:25:25

问题


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;
}

回答1:


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'




回答2:


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;



回答3:


Try this:

class HW_Register
{
public:
    HW_Register(unsigned char nRegisterValue=0)
    {
        Init(nRegisterValue);
    }
    ~HW_Register(void){};

    void Init(unsigned char nRegisterValue)
    {
        nVal = nRegisterValue;
    }

    unsigned Field1() { return nField1; }
    unsigned Field2() { return nField2; }
    unsigned Field3() { return nField3; }

private:
    union
    {
        struct 
        {
            unsigned char nField1:2;
            unsigned char nField2:4;
            unsigned char nField3:2;
        };
        unsigned char nVal;
    };
};


int main()
{
    unsigned char registerValue = 0xFF;
    HW_Register  testRegister(registerValue);

    std::cout << "Field 1 = " << testRegister.Field1() << std::endl;
    std::cout << "Field 2 = " << testRegister.Field2() << std::endl;
    std::cout << "Field 3 = " << testRegister.Field3() << std::endl;

    return 0;
}



回答4:


HW_Register(unsigned char registerValue) : field1(0), field2(0), field3(0) 


来源:https://stackoverflow.com/questions/3562964/how-to-initialize-bitfields-with-a-c-constructor

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!