2014-09-18 2 views
1

Я пытаюсь создать сообщение в буфере фиксированного размера, где пользователь моей библиотеки предоставляет некоторые из данных в нем. Я делал это, предоставляя пользователю указатель на буфер и позволяя им записывать в него и устанавливать аргумент size_t ссылкой на количество написанных байтов. Я хотел отойти от этого подхода, потому что он позволяет пользователю случайно повредить буфер или неправильно сообщить количество записанных байтов. Для того, чтобы сделать это, я сделал следующее:Является ли этот вариант использования CRTP неопределенным поведением?

Defined этой структуры:

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

Это правда! Я попробую это, предоставив загрузчику pointer pos и сразу же выполнив memcpy! –

+0

Это было - поведение исправлено –

0

Это неопределенное поведение, если вы не можете гарантировать, что все классы, полученные из MsgBase, будут равными старыми данными (POD).

В момент, когда производный класс содержит указатель, вы отправляете необработанные указатели в сеть или файл, который бесполезен и, вероятно, смертелен, когда вы перезагружаете содержимое буфера и пытаетесь понять его.

Все, что не является POD, должно быть правильно сериализовано, а не отправлено сырым через границы процесса.


Будьте осторожны с тем, кто выделяет то, что тоже (новые/удалить против таНос против нового байта []/удалить [] ...)

+0

Хорошая точка. У меня есть проверка на это, и я знаю, что все структуры (есть только 4), которые существуют сейчас, являются POD. Они не сильно отличаются от примера выше. Сообщение становится поврежденным до того, как оно сериализуется, и это происходит только в одном бинарнике, но хорошо работает на десятках других. Кроме того, здесь нет выделения, это стек и memcopied. –

Смежные вопросы