Сериализовать структуру с указателем на структуру - PullRequest
0 голосов
/ 25 мая 2020

Я пытаюсь следить за статьей, написанной по адресу https://accu.org/index.php/journals/2317 Мне показалось это интересным теперь, когда я пытаюсь погрузиться в сериализацию / сохранение / загрузку файлов для некоторых административных инструментов. работа над. Я пометил его на c ++, потому что я перенесу его в класс после того, как заставлю его работать (надеюсь, с вашей помощью). библиотека на данный момент. Я хочу знать или попытаться выяснить, работают ли эти вещи. с memcpy.

Прямо сейчас он компилируется, но когда я сериализую и десериализую структуры, данные не совпадают.

Пожалуйста, просмотрите и помогите мне, или укажите на меня справа направление. После кода я постараюсь объяснить, как я это понимаю.

#include <iostream>
#include <cassert>

struct Y
{
    int yy;
};

struct X
{
    int xx;
    struct Y* y = nullptr;
    int z;
};

// Changed OutMemStram and InMemStream for IOMemStream, same data
struct IOMemStream
{
    // changed from uint8_t* to char*
    char* pp;
    char* ppEnd;
};

// Output
inline void WriteToStream( IOMemStream* dst, void* p, size_t sz )
{
    dst->pp = (char*)p; // original code doesn't contain this line
    dst->ppEnd = (char*)p + sz; // original code doesn't contain this line

    assert( (dst->pp + sz) <= dst->ppEnd );
    memcpy( dst->pp, p, sz );
    dst->pp += sz;
}

void SerializeX( IOMemStream* dst, X* x )
{
    WriteToStream( dst, x, sizeof( X ) );
    WriteToStream( dst, x->y, sizeof( Y ) );
}

// Input
inline void ReadFromStream( IOMemStream* src, void* p, size_t sz ) 
{
    //assert( (src->pp + sz) <= src->ppEnd );

    memcpy( p, src->pp, sz );
    src->pp += sz;
}

void DeserializeX( IOMemStream* src, X* x ) 
{
    ReadFromStream( src, x, sizeof( X ) );
    // x->y contains garbage at this point(!)
    // ok, not exactly garbage - but a pointer
     // which is utterly invalid in our current space
    x->y = new Y;
    assert( x->y );
    ReadFromStream( src, x->y, sizeof( Y ) );
}


// Usage sample
int main()
{
    // Assume struct x was previously filled by other function
    X x;
    x.xx = 1000;
    x.z = 2000;
    x.y = new Y;
    x.y->yy = 3000;

    // IO buffer
    IOMemStream ioms;

    // Test for output
    SerializeX( &ioms, &x );

    // Test for input
    X x1;
    DeserializeX( &ioms, &x1 );

    // x1.xx should be 1000 and x1.< should be 2000
    std::cout << x1.xx << ", " << x1.z << std::endl;

    delete x.y;
    delete x1.y;
    //delete ioms.pp; // gets exception

    std::cin.get();
    return 0;
}

Так я понял (или не понял).

  1. Struct X, содержит 2 целых числа и 1 указатель структуры на структуру Y, которая при условии, что int size = 4, размер X будет 12 байтов. Y это 4 байта.
  2. IOMemStream содержит указатели как на структуру X, так и на структуру Y.
  3. IOMemStream-> pp и IOMemStream-> ppEnd должны иметь 12 байтов и 4 байта из структуры X и X -> Y.
  4. Функция WriteToStream упаковывает байты из структур в указатели pp и ppEnd, после назначения X указатель увеличивается на размер, чтобы подготовиться к структуре Y. (в исходной статье утверждение сделано без присвоения переменных pp и ppEnd из
  5. IOMemStream) Функция SerializeX использует WriteToStrea как для структур X, так и для Y.

Почти то же самое для десериализации но в обратном порядке и ReadFromStream выделит память для структуры Y.

Я потерялся отсюда, поскольку значения не совпадают с сериализацией и десериализацией. Также, надеюсь, я правильно понял: D

Заранее спасибо!

Ответы [ 2 ]

1 голос
/ 25 мая 2020

Заявление об ограничении ответственности : Это просто набор комментариев, которые слишком длинные, чтобы их можно было написать как комментарии, вместо правильного ответа.


Все не так просто, и вы есть некоторые неправильные предположения. В частности, даже если int занимает 4 байта, а указатель - 8 байтов, ваша структура X может иметь более 16 байтов из-за заполнения.

Попробуйте добавить приведенный ниже код в свой основной и посмотрите, что вы получите

    X x;
    std::cout << sizeof(x.xx) << std::endl;
    std::cout << sizeof(x.y) << std::endl;
    std::cout << sizeof(x.z) << std::endl;
    std::cout << sizeof(x) << std::endl;

В моей системе я получаю 4 байта для каждого целого числа и 8 байтов для указателя, но для структуры я получаю 24 байта. Причина в заполнении (см. этот вопрос, чтобы узнать больше ). Обратите внимание, что порядок, в котором вы объявляете члены структуры, влияет на заполнение. Если вместо этого вы объявили

struct X {
    struct Y* y = nullptr;
    int xx;
    int z;
};

or

```c++
struct X {
    int xx;
    int z;
    struct Y* y = nullptr;
};

, тогда для структуры потребуется всего 16 байт (по крайней мере, в моей системе).


Другая проблема - сериализация указателей. Очевидно, что от хранения значения указателя мало пользы. Вам нужно будет подумать о том, как обращаться к struct Y. Вы должны создать объект типа Y во время десериализации, а затем поместить его адрес в десериализованную структуру X, но детали могут измениться в зависимости от остальной части программы, и здесь у нас есть только пример кода.


Наконец, будьте осторожны с memcpy, так как он просто сохранит байты (возможно, включая заполнение) и не позаботится о порядке байтов.

0 голосов
/ 28 мая 2020

Я прочитал статью еще немного, и похоже, что данные, которые они получают в объекте потока, поступают с машины, поэтому pp и ppend, вероятно, сохраняют там состояние «до и после». К тому же, кажется, что функции созданы только для этой цели.

Жаль, потому что я подумал, что это хороший код, на котором можно учиться, возможно, меня это расстроило и я не понял концепцию.

Извините, что потратил ваше время, ребята !, но я не сдамся :)

Есть ли книги или сайты, чтобы узнать больше о сериализации, о которых вы знаете? Я не хочу сейчас использовать какие-либо внешние библиотеки, потому что хочу узнать больше и попрактиковаться в использовании собственных инструментов. По какой-то причине использование внешних библиотек кажется мне жульничеством и заставляет думать, что я иду не по правильному пути.

спасибо, привет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...