IlliterateElf
IlliterateElf

Reputation: 5

Integer Bitwise Serialize and Deserialize Both Don't Work

Hoping for some help as I feel like I've been banging my head against a door for ages to no avail.

The Goal: Use bitwise shift operators to extract a single int from a Dog object and Serialize it into a char pointer belonging to SerializedObject, save that char pointer to a .dat file with std::ofstream using std::ios::binary, then read that .dat file to a std::ifstream using std::ios::binary, and finally create a Dog object by Deserializing the SerializedObject.

The Issue:

  1. The bitwise shift of Dog::Serialize doesn't seem to be performing correctly. The first 3 indices of the character pointer are always 0 and the last index is the entire number. The decimal should convert to the binary 1110 or 0000000000001110 but instead it comes out as (0)(0)(0)(14)
  2. Saving to the .dat file does not seem to be working correctly. Even if the tSo->mBuffer is the above it should be saving the text as a single , instead it is saving as PçN
  3. When the .dat file is loaded, it creates a huge number for the dog2->mAge. This likely has to do with the binary of the text it is saving being 01010000 11000011 10100111 01001110 00000001 00001010, instead of just 1110. This combined with how the loop for Dog::CreateFromSO just seems to be multiplying the mAge value seems to be the root cause of this.

I've looked over the program with my peers and we have nearly identical code. Nothing I do in terms of Dog::Serialize or Dog::CreateFromSO seems to have an effect on this result. I am truly at a loss.

Dog.h

#pragma once
#include <list>
#include "SerializedObject.h"

class Dog
{
public:
    static const int SID = 1;
    static Dog* NewDog();
    static void DeleteAll() {}
    static Dog* CreateFromSO(SerializedObject* tSO);
    static Dog* FindDogFromID( int tID ) { return nullptr; }

    int mAge = 0;

    SerializedObject* Serialize();

private:
    int mObjectID = 0;
    Dog();

    static std::list<Dog*> sAllDogs;

};

Dog.cpp

#include "Dog.h"
Dog* Dog::NewDog()
{
    Dog* result = new Dog;
    return result;
}

Dog::Dog() {
    mAge = 14;
    mObjectID = 1;
}
SerializedObject* Dog::Serialize()
{
    SerializedObject* result = new SerializedObject;
    result->mFullSize = sizeof(int);
    result->mClassID = this->SID;

    result->mBufferSize = sizeof(int);
    result->mBuffer = new char[result->mBufferSize];
    result->mBuffer[0] = (mAge >> 24) &0xFF;
    result->mBuffer[1] = (mAge >> 16) &0xFF;
    result->mBuffer[2] = (mAge >> 8) &0xFF;
    result->mBuffer[3] = mAge & 0xFF;
    return result;
}
Dog* Dog::CreateFromSO(SerializedObject* tSO)
{
    Dog* result = new Dog;
    result->mAge = 0;
    for (int i = 0; i < 4; i++)
    {
        result->mAge <<= 8;
        result->mAge |= tSO->mBuffer[i];
    }
    return result;
}

SerializedObject.h

#pragma once
struct SerializedObject
{
    int mFullSize = -1;
    int mClassID = -1;

    int mBufferSize = -1;
    char* mBuffer= nullptr;
};

main.cpp

#include "SerializedObject.h"
int main()
{
    std::ofstream os;
    os.open("APLO123.dat", std::ios::binary);
    Dog* dog1 = Dog::NewDog();
    SerializedObject* tSo =  dog1->Serialize();
    for (int i = 0; i < 4; i++)
    {
        os.put(tSo->mBuffer[i]);
    }
    os.close();

    SerializedObject* tSo2 = new SerializedObject;
    char* buffer = new char[sizeof(int)];
    std::ifstream is;
    is.open("APLO123.dat", std::ios::binary);
    is.read(buffer, sizeof(int));
    tSo2->mBuffer = buffer;
    Dog* dog2 = Dog::CreateFromSO(tSo2);
}

The mBuffer just before Dog::Serialize return The debug shows the values 0 0 0 14

The DatFile Shows the PçN output to .dat

The buffer in main after ifstream.read Is read incorrectly 4 0 0 0

The value of result->mAge just before return of Dog::CreateFromSO Shows how the value is huge: 67108864

EDIT: Added Dog.h, the rest of Dog.cpp and SerializedObject.h

Upvotes: 0

Views: 382

Answers (1)

A M
A M

Reputation: 15265

You serialization and deserialization works.

But there are many many design flaws that need to be fixed.

You are, for reasons that nobody can understand, using new all over the place. Although never necessary. And you never delete your allocated memory. And you do it in such a way that it is really hard to fix that.

Then, you do not understand that member functions should work on its own data members, and not on mebers of other classes.

In your "Dog" class, you have a function that creates a different dog. It does not set the value of "this" dog, but creates a new one. That is wrong. Even the compiler gives you a hint by emitting an error meesage for the line Dog* dog2 = Dog::CreateFromSO(tSo2);. That is simply the wrong design.

If I look at your code, then I can see nowhere that mAge is initialized to some value. You seem to store indeterminate values in the file and then wonder what you read back.

To show your that the basic serialization / deserialization works, please see the below code:

#include <iostream>
#include <fstream>

struct SerializedObject {
    size_t mFullSize{};
    unsigned int mClassID;
    size_t mBufferSize{};
    char* mBuffer{};
};

struct Dog {
    unsigned int SID{};
    unsigned int mAge{};
    SerializedObject* Serialize();
    Dog* CreateFromSO(SerializedObject* tSO);
};

SerializedObject* Dog::Serialize() {
    SerializedObject* result = new SerializedObject;
    result->mFullSize = sizeof(int);
    result->mClassID = this->SID;

    result->mBufferSize = sizeof(int);
    result->mBuffer = new char[result->mBufferSize];
    result->mBuffer[0] = (mAge >> 24) & 0xFF;
    result->mBuffer[1] = (mAge >> 16) & 0xFF;
    result->mBuffer[2] = (mAge >> 8) & 0xFF;
    result->mBuffer[3] = mAge & 0xFF;
    return result;
}
Dog* Dog::CreateFromSO(SerializedObject* tSO) {
    Dog* result = new Dog;
    result->mAge = 0;
    for (int i = 0; i < 4; i++)
    {
        result->mAge <<= 8;
        result->mAge |= tSO->mBuffer[i];
    }
    return result;
}
int main() {
    std::ofstream os;
    os.open("APLO123.dat", std::ios::binary);
    Dog* dog1 = new Dog();

    dog1->SID = 1;
    dog1->mAge = 42;

    SerializedObject* tSo = dog1->Serialize();
    for (int i = 0; i < 4; i++)
    {
        os.put(tSo->mBuffer[i]);
    }
    os.close();

    SerializedObject* tSo2 = new SerializedObject;
    char* buffer = new char[sizeof(int)];
    std::ifstream is;
    is.open("APLO123.dat", std::ios::binary);
    is.read(buffer, sizeof(int));
    tSo2->mBuffer = buffer;
    Dog dogTemp;
    Dog* dog2 = dogTemp.CreateFromSO(tSo2);

    std::cout << dog2->mAge << '\n';
}

But this code is that wrong that it should never be used. Especially the memory leaks are a severe bug.

Unfortunately, you do not give enough information, so that I can show you a complete recoding. Especially because our teacher maybe want you to learn about serialization in general, using a framework or talking about factories later.

Anyway. Here some minmum improved example. Still not good and wrong design . . .

#include <iostream>
#include <fstream>
#include <vector>
#include <string>

struct SerializedObject {
    size_t mFullSize{};
    unsigned int mClassID;
    size_t mBufferSize{};
    std::vector<char> mBuffer{};
};
struct Dog {
    unsigned int SID{};
    unsigned int mAge{};
    SerializedObject Serialize();
    void CreateFromSO(SerializedObject& tSO);
};

SerializedObject Dog::Serialize() {
    SerializedObject result{};
    result.mFullSize = sizeof(int);
    result.mClassID = this->SID;

    result.mBufferSize = sizeof(int);
    result.mBuffer.resize(result.mFullSize);
    result.mBuffer[0] = (mAge >> 24) & 0xFF;
    result.mBuffer[1] = (mAge >> 16) & 0xFF;
    result.mBuffer[2] = (mAge >> 8) & 0xFF;
    result.mBuffer[3] = mAge & 0xFF;
    return result;
}
void Dog::CreateFromSO(SerializedObject& tSO) {
    
    mAge = 0;
    for (int i = 0; i < 4; i++)   {
        mAge <<= 8;
        mAge |= tSO.mBuffer[i];
    }
}

std::string filename{ "APLO123.dat" };

int main() {

    std::ofstream os(filename, std::ios::binary);
    if (os) {
        Dog dog1{ 1,42 };
        SerializedObject tSo = dog1.Serialize();
        for (int i = 0; i < 4; i++)
        {
            os.put(tSo.mBuffer[i]);
        }
        os.close();

        std::ifstream is(filename, std::ios::binary);
        if (is) {

            SerializedObject tSo2;
            tSo2.mBuffer.resize(sizeof(int));
            is.read(tSo2.mBuffer.data(), sizeof(int));

            Dog dog2;
            dog2.CreateFromSO(tSo2);

            std::cout << dog2.mAge << '\n';
        }
        else std::cerr << "\nError: Could not open '" << filename << "' for reading.\n";
    }
    else std::cerr << "\nError: Could not open '" << filename << "' fow writing.\n";
}

Upvotes: 1

Related Questions