2016-04-28 83 views
1

我想在對象緩衝區中使用「Handles」並將其存儲到數據中以減少分配開銷。該句柄只是一個與對象數組的索引。不過,我需要檢測重新分配後的使用情況,因爲這可能很容易滑入。常見的做法似乎是使用位域。然而,這導致了兩個問題:Handle的便攜式位字段

  1. 位字段都是定義
  2. 位轉移是不能跨越的大/小端機便攜。

我需要什麼:

  • 商店手柄文件中以最小的空間
  • 手柄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; 
} 

我的目標是保持手柄不透明的,需要增加一個布爾檢查,但沒有別的。句柄的用戶不應該能夠對它做任何事情,而是傳遞給它。

所以問題我遇到:

  • 聯盟UB - >替換其T_Handle通過Storage並添加一個構造函數從存儲到處理
  • 編譯器如何佈局位域?我填寫整個聯盟/類型,所以不應該填充。所以可能唯一可以不同的是哪種類型首先取決於藥物的性質,是正確的?
  • 如何將handle_存儲到文件中並從可能的不同型號的機器上加載它,並仍然有indexmagic是正確的?我想我可以存儲包含Storage'endian-correct'並得到正確的值,IF兩個成員佔據的空間恰好是一半的空間(2短褲在一個uint)但我總是需要更多的空間爲索引比魔術值。

注意:已經有關於位域和聯合的問題。摘要:

  • 位字段可能會有意想不到的填充(這裏不可能作爲一個整體式佔用)
  • 秩序的「成員」依賴於編譯器(這裏只有2個可能的方式,應保存承擔順序完全取決於字節序,所以這可能實際上可能幫助或不實際)
  • 特定的位二進制佈局可以通過手動移動來實現(或者例如包裝http://blog.codef00.com/2014/12/06/portable-bitfields-using-c11/) - >這裏沒有答案。我還需要一個特定的佈局值IN位域。所以我不確定我得到了什麼,如果我創建一個句柄爲handle = (magic << numIndexBits) | index並將其保存/加載爲二進制(無字節轉換)缺少BigEndian機器進行測試。

注意:沒有C++ 11,但允許提升。

+0

在保存之前通過htonl()運行uint32_t,並且在加載之後通過ntohl()來確保磁盤位總是以存儲的big-endian格式存儲,而不管CPU的端序性如何。 –

+0

這樣我就可以在磁盤上得到'Storage'的相同佈局。但這是否也意味着我可以通過位域或移位安全地訪問其中的2個值? (側面問題:是否有在線服務來測試這些事情?) – Flamefire

+0

C/C++移位操作符總是以數據的本地端表示形式操作,因此該字的端序不會改變它們的語義。當數據從一臺計算機傳遞到另一臺時(例如,通過網絡連接或通過在一臺機器上保存到一個文件並加載到另一臺機器上),端到端只是一個問題,它可以通過htonl()/ ntohl( )作爲保存/恢復例程的一部分,修正了字節順序。 –

回答

0

答案是很簡單(基於另一個問題,我通過@Jeremy Friesner忘記鏈接和評論):

爲「數字」已經在C/C的抽象++一個可以確保始終有當變量在CPU寄存器中時(當它用於任何類似計算時)時的相同位表示法。C中的位移也以不依賴於端部的方式定義。這意味着x << 1總是等於x * 2(因此大端)是 只有時間一個送字節順序問題保存到文件,發送/ recv的通過網絡或從存儲器訪問不同的看法(如通過指針......)

時在這裏不能使用(C)位字段,因爲對於「條目」的順序不能100%確定。如果允許Bitfield容器以「數字」的形式訪問數據,那麼它們可能沒問題。

保存是(仍然)使用bitshifts,在這種情況下非常簡單(只有2個值)在存儲/序列化過程中,數字必須以endian不可知的方式存儲。