[英]Mask from bitfield in C++
这是一个我找不到好的答案的小谜题:
给定一个带有位域的结构,例如
struct A {
unsigned foo:13;
unsigned bar:19;
};
C++ 中是否有一种(可移植的)方法来为其中一个位域获取正确的掩码,最好是作为编译时常量 function 或模板?
像这样:
constinit unsigned mask = getmask<A::bar>(); // mask should be 0xFFFFE000
理论上,在运行时,我可以粗略地做:
unsigned getmask_bar() {
union AA {
unsigned mask;
A fields;
} aa{};
aa.fields.bar -= 1;
return aa.mask;
}
甚至可以将其包装在宏中(讨厌。)以使其“通用”。
但我想您可以很容易地看出这种方法的各种不足之处。
有更好的通用 C++ 方法吗? 或者甚至是一种不太好的方式? 下一个 C++ 标准有什么有用的东西吗? 反射?
编辑:让我补充一点,我正在尝试找到一种使位域操作更加灵活的方法,以便程序员可以使用掩码同时修改多个字段。 我追求简洁的符号,这样就可以在没有大量样板的情况下简洁地表达事物。 将在 I/O 驱动程序中使用硬件寄存器作为一个用例。
不幸的是,没有更好的方法 - 事实上,没有办法通过直接在 C++ 中检查其 memory 从结构中提取单个相邻位字段。
来自Cppreference :
位域的以下属性是实现定义的:
通过使用超出范围的值分配或初始化带符号的位域,或将带符号的位域递增超过其范围而产生的值。
class object 中位域的实际分配细节
- 例如,在某些平台上,位域不跨越字节,而在其他平台上
- 此外,在某些平台上,位字段是从左到右打包的,在其他平台上是从右到左打包的
你的编译器可能会给你更强的保证; 但是,如果您确实依赖于特定编译器的行为,则不能指望您的代码可以与不同的编译器/体系结构对一起工作。 据我所知,GCC 甚至没有记录它们的位字段打包,而且它因架构而异。 因此,您的代码可能适用于 x86-64 上特定版本的 GCC,但实际上会破坏其他所有版本,包括同一编译器的其他版本。
如果您真的希望能够以通用方式从随机结构中提取位域,最好的办法是传递一个 function 指针(而不是掩码); 这样,function 就可以安全地访问该字段并将值返回给它的调用者(或设置一个值)。
像这样:
template<typename T>
auto extractThatBitField(const void *ptr) {
return static_cast<const T *>(ptr)->m_thatBitField;
}
auto *extractor1 = &extractThatBitField<Type1>;
auto *extractor2 = &extractThatBitField<Type2>;
/* ... */
现在,如果你有一对{pointer, extractor}
,你可以安全地获取位域的值。 (当然,提取器 function 必须匹配该指针后面的 object 的类型。)与使用{pointer, mask}
对相比,开销并不大; function 指针可能比 64 位机器上的掩码大 4 个字节(如果有的话)。 提取器 function 本身将只是一个 memory 加载、一些微调和一个返回指令。 它仍然会非常快。
这是可移植的,并受 C++ 标准支持,这与直接检查位域的位不同。
或者,C++ 允许在具有共同初始成员的标准布局结构之间进行转换。 (尽管请记住,一旦 inheritance 或私人/受保护成员参与其中,这就会崩溃,上面的第一个解决方案也适用于所有这些情况。)
struct Common {
int m_a : 13;
int m_b : 19;
int : 0; //Needed to ensure the bit fields end on a byte boundary
};
struct Type1 {
int m_a : 13;
int m_b : 19;
int : 0;
Whatever m_whatever;
};
struct Type2 {
int m_a : 13;
int m_b : 19;
int : 0;
Something m_something;
};
int getFieldA(const void *ptr) {
//We still can't do type punning directly due
//to weirdness in various compilers' aliasing resolution.
//std::memcpy is the official way to do type punning.
//This won't compile to an actual memcpy call.
Common tmp;
std::memcpy(&tmp, ptr, sizeof(Common));
return tmp.m_a;
}
另请参阅: memcpy 可以用于类型双关吗?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.