我有很多布尔属性的对象,所以我使用位域来打包更紧凑的属性。我还希望能够以紧凑的方式序列化和反序列化这些属性,例如不是逐字段,而是通过序列化和反序列化保存字段的64位uint。这种方式不仅快得多(例如避免所有移位和填充),而且内存效率提高了8倍。
但是,我读到该标准并不能保证位域实现在不同平台上是统一的。我可以期望位域容器的“批量”二进制序列化将跨平台产生统一的结果吗?或者在使用属性时进行手动移动和屏蔽可能更安全,以便可以进行批量序列化和反序列化?
答案 0 :(得分:1)
您可以查看std::bitset:
它为cast your bits into an unsigned long long和create a bitset from a stored unsigned long long提供了明确定义的功能。定义了位集中的第一位是ullong
表示的最低有效位。
所以你可以有类似的东西:
std::bitset<N> bits;
unsigned long long val = bits.to_ullong();
// serialize your ullong value
// load ullong from serialized data
unsigned long long val2 = ...;
std::bitset<N> newBits(val2);
因此,只要您的序列化可以正确加载/存储unsigned long long
,您就可以了。
唯一的问题是当你有一个比unsigned long long
太大的位域时。在这种情况下,标准没有提供提取位域的简单方法。
答案 1 :(得分:1)
一种可能性是使用ASN.1通过BIT STRING处理此问题。它以与本地表示无关的方式精确定义序列化。这使得它可以跨平台保持一致,无论本地平台是big-endian还是little-endian。您可以在http://asn1-playground.oss.com使用免费的在线ASN.1编译器和编码器/解码器来查看生成的序列化。
ASN.1允许您提供&#34;名称&#34;每个位都可以轻松设置或检查位串中的每个命名位。
答案 2 :(得分:0)
平台的字节序变化表明任何这样的序列化都是不可移植的。基于此我会说你不能指望位域容器的批量二进制序列化在不同平台上是统一的。
解决方案必须考虑位排序并根据平台进行更正。
答案 3 :(得分:0)
这可能会对您有所帮助,这只是各种序列化类型的一个小例子。 我添加了位集和原始位值,可以像下面这样使用。
(所有示例在https://github.com/goblinhack/simple-c-plus-plus-serializer)
class BitsetClass {
public:
std::bitset<1> a;
std::bitset<2> b;
std::bitset<3> c;
unsigned int d:1; // need c++20 for default initializers for bitfields
unsigned int e:2;
unsigned int f:3;
BitsetClass(void) { d = 0; e = 0; f = 0; }
friend std::ostream& operator<<(std::ostream &out,
Bits<const class BitsetClass & > const m
{
out << bits(my.t.a);
out << bits(my.t.b);
out << bits(my.t.c);
std::bitset<6> s(my.t.d | my.t.e << 1 | my.t.f << 3);
out << bits(s);
return (out);
}
friend std::istream& operator>>(std::istream &in,
Bits<class BitsetClass &> my)
{
std::bitset<1> a;
in >> bits(a);
my.t.a = a;
in >> bits(my.t.b);
in >> bits(my.t.c);
std::bitset<6> s;
in >> bits(s);
unsigned long raw_bits = static_cast<unsigned long>(s.to_ulong());
my.t.d = raw_bits & 0b000001;
my.t.e = (raw_bits & 0b000110) >> 1;
my.t.f = (raw_bits & 0b111000) >> 3;
return (in);
}
};