3

I'm storing my class object in the binary file but I'm getting weird results when I load the data.
Following Code is Loading and Saving Data:

#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <sstream>
using namespace std;

template <class C>
void Load(const char fileName[], C& obj)
{
    ifstream in;
    in.open(fileName, ios::in | ios::binary);
    in.read(reinterpret_cast<char*>(addressof(obj)), sizeof(obj));
    in.close();
    return;
}

template <class T>
void Save(const char fileName[], T obj)
{
    ofstream out;
    out.open(fileName, ios::out | ios::binary);
    out.write(reinterpret_cast<char const*>(addressof(obj)), sizeof(obj));
    stringstream ss;
    out.close();
    return;
}

class Contact {
public:
    int CompareTo(Contact obj)
    {
        return 1;
    }
    string ss;
    int rollNum;
};

class Data {
public:
    Data()
    {
    }
    Contact arr[10];
};

int main()
{
    const char fileName[] = "ContactMG.dat";
    /*
     Data *T = new Data();
    
     for(int i=0;i<10;i++)
          T->arr[i].ss = "fahad";
       Save(fileName , *T);
    */

    Data* d = new Data();
    Load(fileName, *d);
    for (int i = 0; i < 10; i++)
        cout << d->arr[i].ss << endl;
}

/*
 Console outPut:
ⁿx

 p²x
   σß╥Z∙
  ░▒▓│┤
   >
☺Y╩
░‼╩

*/

/* Binary File
   @®     ®     ®     
*/

I want to ask how I can store this object in the binary file and load it?

I'm pretty sure the problem is with string but I don't know how to fix it! I have already known to store strings in binary files but don't know how to store class objects which have string in it

19
  • 5
    This can't possibly work since Contact is not a POD type. A std::string will contain pointers to the actual data. When you saved this you saved the pointers not the data. Commented Dec 18, 2021 at 17:44
  • 1
    An alternate is this question which shows you how to serialize a class containing a std::string : https://stackoverflow.com/questions/7046244/serializing-a-class-which-contains-a-stdstring Commented Dec 18, 2021 at 17:57
  • 2
    For development purposes, I recommend writing & reading to a text format first. Possibly a structured format, like XML or JSON or YAML or one of your own devising that is suitable for your purposes. Get that working. Then consider if it is worth it to have a binary format for reading & writing. Commented Dec 18, 2021 at 18:00
  • 1
    do you have any advice to store singleton class in the file easily? Use a text format and JSON or XML and a library for that. Commented Dec 18, 2021 at 18:02
  • 2
    In addition to Eljay and drescherjm comments: Start with YAML / JSON; if you need to stay with a binary format, e.g. because of performance reasons, you might want to have a look at Google's Protocol Buffers: developers.google.com/protocol-buffers Commented Dec 18, 2021 at 19:36

1 Answer 1

5
+100

I would introduce a new level of indirection, i.e. functions from_binary and to_binary, and implement your Load and Store in terms of those:

template <class C>
bool Load(const char fileName[], C& obj) {
    if (ifstream in{fileName, ios::in | ios::binary}) {
        from_binary(in, obj);
        return true;
    }

    return false;
}

template <class T>
bool Save(const char fileName[], T obj) {
    if (ofstream out{fileName, ios::out | ios::binary}) {
        to_binary(out, obj);
        return true;
    }

    return false;
}

For POD data types, from_binary and to_binary will just do what you already did in Load/Store (beware, however: pointers are PODs but saving an address is pretty much meaningless):

template <class T, typename = enable_if_t<is_pod_v<T>>>
void from_binary(ifstream& in, T& obj) {
    in.read(reinterpret_cast<char*>(addressof(obj)), sizeof(obj));
}

template <class T, typename = enable_if_t<is_pod_v<T>>>
void to_binary(ofstream& out, T const& obj) {
    out.write(reinterpret_cast<char const*>(addressof(obj)), sizeof(obj));
}

As pointed out in the comments, std::string is not a POD type. I'm going to serialize it by saving the character count and then the actual characters:

void from_binary(ifstream& in, string& str) {
    std::size_t stringSize{0};
    from_binary(in, stringSize);

    str.reserve(stringSize);
    for (size_t i = 0; i != stringSize; ++i) {
        char ch{};
        in.read(&ch, 1);
        str.push_back(ch);
    }
}

void to_binary(ofstream& out, string const& str) {
    auto const stringSize = str.size();
    to_binary(out, stringSize);

    auto const* cStr = str.c_str();
    out.write(cStr, stringSize);
}

Also, I'm going to serialize/deserialize an array by calling to_binary/from_binary on each element of the array:

template <class T, size_t N>
void from_binary(ifstream& in, T (&obj)[N]) {
    for (auto& elem : obj) from_binary(in, elem);
}

template <class T, size_t N>
void to_binary(ofstream& out, T const (&obj)[N]) {
    for (auto const& elem : obj) to_binary(out, elem);
}

The above functions are enough to implement from_binary and to_binary for your Contact and Data classes:

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

using namespace std;

template <class T, typename = enable_if_t<is_pod_v<T>>>
void from_binary(ifstream& in, T& obj) {
    in.read(reinterpret_cast<char*>(addressof(obj)), sizeof(obj));
}

template <class T, typename = enable_if_t<is_pod_v<T>>>
void to_binary(ofstream& out, T const& obj) {
    out.write(reinterpret_cast<char const*>(addressof(obj)), sizeof(obj));
}

void from_binary(ifstream& in, string& str) {
    std::size_t stringSize{0};
    from_binary(in, stringSize);

    str.reserve(stringSize);
    for (size_t i = 0; i != stringSize; ++i) {
        char ch{};
        in.read(&ch, 1);
        str.push_back(ch);
    }
}

void to_binary(ofstream& out, string const& str) {
    auto const stringSize = str.size();
    to_binary(out, stringSize);

    auto const* cStr = str.c_str();
    out.write(cStr, stringSize);
}

template <class T, size_t N>
void from_binary(ifstream& in, T (&obj)[N]) {
    for (auto& elem : obj) from_binary(in, elem);
}

template <class T, size_t N>
void to_binary(ofstream& out, T const (&obj)[N]) {
    for (auto const& elem : obj) to_binary(out, elem);
}

template <class C>
bool Load(const char fileName[], C& obj) {
    if (ifstream in{fileName, ios::in | ios::binary}) {
        from_binary(in, obj);
        return true;
    }

    return false;
}

template <class T>
bool Save(const char fileName[], T obj) {
    if (ofstream out{fileName, ios::out | ios::binary}) {
        to_binary(out, obj);
        return true;
    }

    return false;
}

class Contact {
   public:
    int CompareTo(Contact obj) { return 1; }
    string ss;
    int rollNum;
};

void from_binary(ifstream& in, Contact& obj) {
    from_binary(in, obj.ss);
    from_binary(in, obj.rollNum);
}

void to_binary(ofstream& out, Contact const& obj) {
    to_binary(out, obj.ss);
    to_binary(out, obj.rollNum);
}

class Data {
   public:
    Data() {}
    Contact arr[10];
};

void from_binary(ifstream& in, Data& obj) { from_binary(in, obj.arr); }

void to_binary(ofstream& out, Data const& obj) { to_binary(out, obj.arr); }

int main() {
    const char fileName[] = "ContactMG.dat";

    {
        Data data;

        auto const contactCount = sizeof(data.arr) / sizeof(data.arr[0]);
        for (size_t c = 0; c != contactCount; ++c) {
            data.arr[c].ss = "some name " + to_string(c);
            data.arr[c].rollNum = c;
        }

        Save(fileName, data);
    }

    {
        Data data;
        Load(fileName, data);

        for (auto const& contact : data.arr)
            cout << "Contact: rollNum=" << contact.rollNum
                 << ", ss=" << contact.ss << '\n';
    }
}

Output:

Contact: rollNum=0, ss=some name 0
Contact: rollNum=1, ss=some name 1
Contact: rollNum=2, ss=some name 2
Contact: rollNum=3, ss=some name 3
Contact: rollNum=4, ss=some name 4
Contact: rollNum=5, ss=some name 5
Contact: rollNum=6, ss=some name 6
Contact: rollNum=7, ss=some name 7
Contact: rollNum=8, ss=some name 8
Contact: rollNum=9, ss=some name 9

Although this may solve your particular issue, the number of overloads you'll need for from_binary and to_binary will grow very rapidly as your project grows. So I'd definetely check if there's a more comprehensive (and well tested) library solution out there.

Sign up to request clarification or add additional context in comments.

4 Comments

Hey thanks, paolo please consider up vote if you find this answer helpful so it can help others
one more question can you share some c++ learning sites where I can learn c++ like you
There are many great resources you may look at, e.g. any book from Stroustrup will do. I also find Scott Mayers books very clear. I recommend you keep cppreference and the C++ Core Guidelines always open in your brwser. The CppCon channel on YouTube is another great source of information: the Back To The Basics videos are expecially useful for beginners (and often for experienced developers too).
Thanks, @paolo. can you check this question also please vote this up if you find this helps many people vote down due to in-depth questions and that's why I got banned from question ask stackoverflow.com/questions/70997431/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.