这是我的问题,我有一个结构(我无法改变),如下所示:
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返回要使用的位掩码和偏移量来访问结构的右侧字段,但我认为这有点棘手而且不那么优雅。 你有什么建议吗?
答案 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不是那么优雅,每次更改结构时都需要更新,但是我没有看到自动方式(可能使用反射)来解决这个问题问题