2014-09-18 61 views
1

我試圖在固定大小的緩衝區中創建一條消息,其中我的庫的用戶提供了其中的一些數據。我曾經這樣做是通過給用戶一個指向緩衝區的指針並讓它們寫入它,並通過引用他們寫入的字節數來設置參數size_t。我想擺脫這種方式,因爲它允許用戶意外地破壞緩衝區,或者錯誤地報告寫入的字節數。爲了做到這一點,我做了以下內容:這個CRTP用例是否被認爲是未定義的行爲?

定義這個結構:

template <class Derived> 
struct MsgBase 
{ 
    size_t size() const { return sizeof(Derived); } 
    const char* data() const { 
     const Derived* dat = static_cast<const Derived*>(this); 
     return reinterpret_cast<const char*>(dat); 
    } 
}; 

,我需要的是,如果用戶想要發送某些數據,即它們確定了結構從這個繼承與數據將被寄出。例如:

struct Example : MsgBase<Example> 
{ 
    int a; 
    double b; 
    char c[7]; 
}; 

我有這個類中定義,以幫助他們將數據傳遞到我的圖書館:

class Loader 
{ 
public: 
    Loader() : size(0), data(0) {} 

    size_t size() const { return size; } 
    const char* data() const { return data; } 

    template<class T> void loadData(const T& t) { 
     size = t.size(); 
     data = t.data(); 
    } 

private: 
    size_t size; 
    const char* data; 
}; 

所以我稱他們是這樣的:

{ 
    //pos is a char* to a point in a buffer of data 
    Loader loader; 
    onLibraryCall(&loader); 
    memcpy(pos, loader.data(), loader.size()); 
} 

而且用戶正在這樣做:

void onLibraryCall(Loader* loader) 
{ 
    Example e; 
    e.a = 3; 
    e.b = 2.7; 
    e.c[0] = //bla fill out some stuff here 

    loader->loadData(e); 
} 

這已經在我測試過的無數的二進制文件中使用不同版本的gcc進行編譯,但是在一個特定的二進制文件中一致地破壞了上面的消息。 gdb和valgrind根本沒有幫助我,如果我嘗試記錄上述呼叫的情況,問題就會消失。這讓我覺得這裏有未定義的行爲,但我並不完全確定這可能是什麼,或者我可以做些什麼來進一步調試它?

我有一個檢查,以確保任何這樣定義的結構是POD。我也知道所有的結構是什麼,現在它們都只是整體類型和固定大小的小陣列的組合。

回答

2

Loader::loadData()您通過存儲參數的this指針的副本MsgBase::data()

onLibraryCall()中,您在堆棧上分配了一個Example實例,然後將引用傳遞給Loader::loadData()Example實例在該函數結束時超出範圍並被銷燬。

調用代碼,後onLibraryCall()返回時,memcpy()呼叫從在Loader::loadData()緩存的指針讀取,但指針現在指向是不再使用一個內存地址,所以你有未定義的行爲。

+0

這是真的!我將通過給加載器指針pos並試着立即執行memcpy來試試這個! – 2014-09-18 16:10:35

+0

這是它 - 行爲固定 – 2014-09-18 18:36:31

0

如果您不能保證從MsgBase派生的所有類都是普通舊數據(POD),那麼這是未定義的行爲。

派生類包含一個指針的時刻,你發送原始指針到網絡或文件,當你重新加載緩衝區的內容並嘗試理解它時,這是無用的,可能是致命的。

任何不是POD的東西都應該正確地序列化,而不是通過進程邊界發送原始數據。


小心誰分配什麼,太(新/刪除VS的malloc VS新的byte [] /刪除[] ...)

+0

好點。我有一個檢查,我知道現在存在的所有結構(只有4個)是POD。實際上它們與上面的例子並沒有很大的不同。消息在被序列化之前會被破壞,它只發生在一個單獨的二進制文件上,但在其他幾十個文件上運行良好。另外,這裏沒有分配,它是堆棧和memcopied。 – 2014-09-18 15:52:45