如何使用位域从C ++结构中提取值

时间:2014-07-16 21:39:27

标签: c++ bitmask

这是我的问题,我有一个结构(我无法改变),如下所示:

struct X
{
    uint8_t fieldAB;
    uint8_t fieldCDE;
    uint8_t fieldFGH;
    ...
}

此结构的每个字段包含使用位掩码(位域)打包的不同值,例如fieldAB在hi / lo半字节中包含两个不同的值(A和B),而fieldCDE包含三个不同的值(C,D和E,带有以下位掩码:位7-6,位5-4-3,位2-1-0)等等......

我想编写一个简单的API来使用 enum 读取和写入此值,以便轻松访问每个字段的值:

getValue(valueTypeEnum typeOfValue, X & data);
setValue(valueTypeEnum typeOfValue, X & data, uint8_t value);

枚举valueTypeEnum是这样的:

enum valueTypeEnum
{
    A,
    B,
    C,
    D,
    E,
    ...
}

我的想法是使用一个map(字典)给定valueTypeEnum返回要使用的位掩码和偏移量来访问结构的右侧字段,但我认为这有点棘手而且不那么优雅。 你有什么建议吗?

4 个答案:

答案 0 :(得分:1)

我可以想到一些可以实现的方法,最简单的方法是直接在结构中使用位域:

struct X {
   uint32_t A : 4; // 4 bits for A. 
   uint32_t B : 4; 
   uint32_t C : 4; 
   uint32_t D : 4; 
   uint8_t E : 7; 
   uint8_t F : 1;
};

然后您可以使用例如:

轻松获取或设置值
X x;
x.A = 0xF;

另一种方法可能是直接在宏或内联函数中对其进行编码,但我想你所寻找的可能是位域。

正如评论中所指出的,位字段的实际行为可能取决于您的平台,因此如果空间至关重要,则应检查其行为是否符合您的预期。有关C ++中位字段的更多信息,请Also see here

答案 1 :(得分:1)

我将更多地挖掘位域:

您的X结构保持不变:

struct X
{
    uint8_t fieldAB;
    uint8_t fieldCDE;
    uint8_t fieldFGH;
};

让我们定义一个易于翻译的联盟:

union Xunion {
    X x;
    struct Fields { // Named in case you need to sizeof() it
        uint8_t A   : 4;
        uint8_t B   : 4;

        uint8_t C   : 2;
        uint8_t D   : 3;
        uint8_t E   : 3;

        uint8_t F   : 2;
        uint8_t G   : 3;
        uint8_t H   : 3;
    };
};

现在您可以方便地访问这些位域。

在任何人试图让我活着之前,请注意,这绝不是可移植的,甚至也不是由C ++标准定义的。但它会在任何理智的编译器上做你期望的事情。

您可能希望向__attribute__((packed))结构添加特定于编译器的包装指令(例如GCC' Fields),以及static_assert以确保{{1}两个联盟成员都严格相等。

答案 2 :(得分:0)

你最好的选择IMO是忘记使用结构,或者如果是这样的话,使用结构和字节数组的并集。然后让您的访问函数使用字节数组,它可以很容易地计算偏移量等等。这样做我相信你可以保证访问你想要的字节。

缺点是你必须重新组合结构中的16位和32位值(如果有的话),并记住任何字节序问题。如果您知道在16位或32位地址边界上找到任何此类值,则可以使用union& d短数组和长数组来执行此操作,这可能是最好的,尽管有些不透明。

HTH

答案 3 :(得分:0)

也许我找到了解决方案。 我可以创建如下的API:

uint8_t getValue(valueTypeEnum typeOfValue, X * data)
{
    uint8_t bitmask;
    uint8_t * field = getBitmaskAndFieldPtr(typeOfValue, data, &bitmask);

    return ((*field) & bitmask) >> ...;
}

void setValue(valueTypeEnum typeOfValue, X * data, uint8_t value)
{
    uint8_t bitmask;
    uint8_t * field = getBitmaskAndFieldPtr(typeOfValue, data, &bitmask);

    *field = ((*field) & !bitmask) | (value << ...);
}

uint8_t * getBitmaskAndFieldPtr(valueTypeEnum typeOfValue, X * data, uint8_t * bitmask)
{
    uint8_t * fieldPtr = 0;

    switch(typeOfValue)
    {
         case A:
         {
               *bitmask = 0x0F; // I can use also a dictionary here
               fieldPtr = &data.AB;
               break;
         }
         case B:
         {
               *bitmask = 0xF0; // I can use also a dictionary here
               fieldPtr = &data.AB;
               break;
         }
         case C:
         {
               *bitmask = 0xC0; // I can use also a dictionary here
               fieldPtr = &data.CDE;
               break;
         }
         ...    
    }

    return fieldPtr;
}

我知道switch-case不是那么优雅,每次更改结构时都需要更新,但是我没有看到自动方式(可能使用反射)来解决这个问题问题