句柄

时间:2016-04-28 20:24:01

标签: c++ portability endianness unions bit-fields

我想使用并将“Handles”存储到对象缓冲区中的数据以减少分配开销。句柄只是带有对象的数组的索引。但是我需要检测重新分配后的使用情况,因为这可能很容易发生。常见的方法似乎是使用位字段。然而,这导致了两个问题:

  1. 位字段是实现定义的
  2. 在大/小端机器上不能移植位移。
  3. 我需要什么:

    • 将句柄存储到文件(文件处理程序可以管理整数类型(字节交换)或字节数组)
    • 以最小的空间在句柄中存储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;
    }
    

    我的目标是尽可能保持手柄不透明,添加一个布尔检查,但没有别的。句柄的用户不应该对它做任何事情,而是传递它。

    所遇到的问题:

    • Union是UB - &gt;将T_Handle替换为Storage,然后将“ctor”添加到“存储处理”
    • 编译器如何布局位字段?我填写整个联合/类型,所以应该没有填充。所以可能唯一不同的是,哪种类型首先取决于endianess,对吗?
    • 如何将handle_存储到文件中并从可能的不同字节序机器加载,并且indexmagic是否正确?我想我可以存储包含Storage'endian-correct'并获得正确的值, IF 两个成员占据正好一半的空间(2个短片在一个uint)但我总是想要更多的空间对于指数而不是魔术值。

    注意:已经有关于位域和联合的问题。总结:

    • Bitfields可能有意想不到的填充(这里不可能占用整个类型)
    • “成员”的顺序取决于编译器(这里只有2种可能的方法,应该保存以假设顺序完全取决于字节顺序,所以这可能会或可能不会在这里提供帮助)
    • 比特的特定二进制布局可以通过手动移位(或例如包装http://blog.codef00.com/2014/12/06/portable-bitfields-using-c11/) - &gt;来实现。这里不是答案。我还需要一个特定的值 IN 位域布局。所以我不确定我得到了什么,如果我创建一个句柄handle = (magic << numIndexBits) | index并将其保存/加载为二进制(无结束转换)缺少BigEndian计算机进行测试。

    注意:没有C ++ 11,但允许加速。

1 个答案:

答案 0 :(得分:1)

答案非常简单(基于另一个问题,我忘记了@Jeremy Friesner的链接和评论):

由于“数字”已经是C ++中的抽象,当变量在CPU寄存器中时(当它用于任何计算时),可以确保始终具有相同的位表示。此外,C ++中的位移定义在一种与字节无关的方式。这意味着x << 1总是等于x * 2(因此也就是大端) 只有一次获得endianess问题是保存到文件,通过网络发送/ recv或以不同方式从内存访问它(例如通过指针......)

这里不能使用C ++位域,因为不能100%确定“条目”的顺序。如果Bitfield容器允许以“数字”访问数据,那么它可能没问题。

Savest(仍然)使用位移,在这种情况下非常简单(只有2个值)在存储/序列化期间,必须以与字节序无关的方式存储数字。