Handles的可移植位字段
Portable bit fields for Handles
我想在对象缓冲区中使用并存储数据的"Handles",以减少分配开销。句柄只是一个带对象的数组的索引。然而,我需要在重新分配后检测使用情况,因为这可能很容易出现。常见的方法似乎是使用位字段。然而,这导致了两个问题:
- 位字段由实现定义
- 位移位在大/小端序机器之间是不可移植的
我需要什么:
- 存储文件句柄(文件句柄可以管理整数类型(字节交换)或字节数组)
- 在句柄中以最小空间存储2个值
我得到的:
template<class T_HandleDef, typename T_Storage = uint32_t>
struct Handle
{
typedef T_HandleDef HandleDef;
typedef T_Storage Storage;
Handle(): handle_(0){}
private:
const T_Storage handle_;
};
template<unsigned T_numIndexBits = 16, typename T_Tag = void>
struct HandleDef{
static const unsigned numIndexBits = T_numIndexBits;
};
template<class T_Handle>
struct HandleAccessor{
typedef typename T_Handle::Storage Storage;
typedef typename T_Handle::HandleDef HandleDef;
static const unsigned numIndexBits = HandleDef::numIndexBits;
static const unsigned numMagicBits = sizeof(Storage) * 8 - numIndexBits;
/// "Magic" struct that splits the handle into values
union HandleData{
struct
{
Storage index : numIndexBits;
Storage magic : numMagicBits;
};
T_Handle handle;
};
};
例如,一种用法是:
typedef Handle<HandleDef<24> > FooHandle;
FooHandle Create(unsigned idx, unsigned m){
HandleAccessor<FooHandle>::HandleData data;
data.idx = idx;
data.magic = m;
return data.handle;
}
我的目标是保持句柄尽可能不透明,添加bool检查,但不添加其他内容。句柄的用户应该不能对它做任何事情,只能四处传递它。
所以我遇到的问题:
- 并集是UB->用
Storage
替换其T_Handle
,并从存储添加一个ctor到Handle - 编译器如何布局位字段?我填充了整个联合/类型,所以不应该有填充。因此,可能唯一不同的是,根据endianes,哪种类型排在第一位,对吗
- 如何将
handle_
存储到一个文件中,并从可能不同的endianes机器加载它,同时index
和magic
仍然正确?我想我可以存储包含的Storage
"endian-correct"并获得正确的值,IF两个成员正好占据了一半的空间(uint中有2个Shorts)。但我总是希望索引的空间比魔术值的空间大
注意:关于位域和并集已经存在问题。摘要:
- 位字段可能有意外的填充(此处不可能为整个类型占用)
- "成员"的顺序取决于编译器(这里只有两种可能的方式,应该保存为假设顺序完全取决于endianes,所以这在这里可能有帮助,也可能没有帮助)
- 比特的特定二进制布局可以通过手动移位(或例如包装器)来实现http://blog.codef00.com/2014/12/06/portable-bitfields-using-c11/)->这里没有答案。我还需要IN位字段的值的特定布局。因此,我不确定我得到了什么,例如,如果我创建一个句柄为
handle = (magic << numIndexBits) | index
,并将其保存/加载为二进制(无字节序转换)缺少一个用于测试的BigEndian机器
注意:没有C++11,但允许升压。
答案很简单(基于另一个问题,我忘记了@Jeremy Friesner的链接和评论):
由于"数字"在C++中已经是一种抽象,当变量在CPU寄存器中时(当它用于任何类似的计算时),可以确保始终具有相同的位表示。C++中的位偏移也是以独立于endian的方式定义的。这意味着x << 1
总是等于x * 2
(因此big-endian)只有在保存到文件、通过网络发送/recv或以不同方式(例如通过指针…)从内存访问文件时,才会出现endianes问题
这里不能使用C++位字段,因为不能100%确定"条目"的顺序。如果比特字段容器允许以"数字"形式访问数据,那么它们可能是可以的。
Savest(仍然)使用比特移位,在这种情况下非常简单(只有2个值)。在存储/序列化过程中,数字必须以endian不可知的方式存储。
相关文章:
- 将结构字段的类型展开为可变模板参数
- C++Union/Struct位域的实现和可移植性
- 将位字段导出到数组
- 为了方便起见,我应该避免公开私有字段变量吗
- 当字段可以为null时,如何使用C++接口在Avro中写入数据
- C++ win32 如何使密码字段可选并启用复制和粘贴?
- Djinni 记录是否包含可选的接口字段
- 类内非静态字段初始化 + 对象池 - >降低可维护性/可读性
- ASN.1用可选字段编码
- 协议缓冲区(protobuf)v3.0.0-阿尔法-2中的可选字段和约束
- Handles的可移植位字段
- 可移植可执行文件是否可以直接绝对引用代码段中的外部库,而不是通过 IAT
- Protobuf:如何在C++中处理嵌套(自定义)可选字段
- protobuf-net 缺少可选字段的has_函数
- 如何为未在协议中赋值的可选字段分配空间
- 可移植的c++ 11方法,同时等待套接字和bool变量
- 如何创建可获取模板类型的数据sa类字段
- UML 与泛型和可为空字段的关系
- 用户界面-Adobe Adam and Eve,C++:如何创建可点击按钮、轨迹栏、文本输入字段和图像
- 我该如何使我的分配器可重新绑定?我能做到这一点,同时保持其字段的私有