2014-09-18 54 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