Saving and loading objects from classes in C++

Jan 17, 2020 at 11:17pm
I would like to save objects from a parent class in a text file and then be able to load the objects. The problem is that all the objects are pointers so only the addresses of the objects could be saved but not the objects themselves.

For example:

Car {

int hp;
Gear *x; // this object would in my case be a pointer

}

Gear {

double v;
}

So I would like to, for example, save x but x would in my case be a pointer. I have read up on boost libraries and serialization but haven't been able to come up with a solution. I am hoping to find a way to have different levels of saving content. Say, if I want to save everything or just want to save specific parameters. Then loading either everything or just the specific parameters.
Jan 18, 2020 at 12:41am
Would it be an option, saving the objects together with its objects pointed to?
Like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<iostream>
#include<fstream>

class Gear
{
public:
    friend std::ostream & operator<<( std::ostream &, const Gear &);
    friend std::istream & operator>>( std::istream &, Gear &);
private:
    double v;
};
class Car
{
public:
    friend std::ostream & operator<<( std::ostream &, const Car &);
    friend std::istream & operator>>( std::istream &, Car &);
    ~Car() { delete x; }
private:
    int hp;
    Gear * x;
};

std::ostream & operator<<( std::ostream & os, const Gear & gear)
{
    os << '{' << gear.v << '}';
    return os;
}
std::istream & operator>>( std::istream & is, Gear & gear)
{
    char dump;
    is >> dump >> gear.v >> dump;
    return is;
}

std::ostream & operator<<( std::ostream & os, const Car & car)
{
    os << "{" << car.hp << ',' << *(car.x) << '}';
    return os;
}
std::istream & operator>>( std::istream & is, Car & car)
{
    char dump;
    Gear * gear = new Gear;
    is >> dump >> car.hp >> dump >> *gear >> dump;
    car.x = gear;
    return is;
}

#include<sstream>
#include<vector>

int main()
{
    std::istringstream iss( "{90,{5}}" );
    std::vector<Car> v;
    Car tmp;
    while( iss >> tmp ) v.push_back(tmp);
    for( auto car : v ) std::cout << car << '\n';
}
Jan 18, 2020 at 6:04am
Boost::Serialization is your answer. You should read up on it more, since it does precisely what you're asking, and handles pointers quite well, even polymorphic pointers.

Here's a quick example I whipped up. A lot of the bloat comes from just filler access code and polymorphic serialization.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <boost/filesystem.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/export.hpp>
#include <fstream>
#include <iostream>


struct Person;
void savePerson(Person& person);
void loadPerson(Person& person);

class Race {
public:
	Race() {}
	Race(std::string name) : raceName(name) { }
	friend class boost::serialization::access;
        template<typename Archive>
	void serialize(Archive& ar, const unsigned version)
	{
		ar & raceName;
	}
	std::string raceName;
};

class Human : public Race {
public:
	Human() : Race("Human") {}
	friend class boost::serialization::access;
        template<typename Archive>
	void serialize(Archive& ar, const unsigned version)
	{
	  ar & boost::serialization::base_object<Race>(*this);
	}
};

class Alien : public Race {
public:
	Alien() : Race("Alien") {}
	friend class boost::serialization::access;
        template<typename Archive>
	void serialize(Archive& ar, const unsigned version)
	{
	  ar & boost::serialization::base_object<Race>(*this);
	}
};

class Person
{
public:
	std::string name;
	Race* race;

	friend class boost::serialization::access;
        template<typename Archive>
	void serialize(Archive& ar, const unsigned version)
	{
		ar & name;
		ar & race;
	}
};

BOOST_CLASS_EXPORT_GUID(Human, "Human");
BOOST_CLASS_EXPORT_GUID(Alien, "Alien");


int main()
{
	Person myPerson;
	myPerson.name = "Test";
	myPerson.race = new Alien();

	savePerson(myPerson);

	Person newPerson;
	loadPerson(newPerson);

	std::cout << newPerson.race->raceName;
}

void savePerson(Person& person)
{
	std::ofstream outputStream;
	outputStream.open("myPerson.txt");
	boost::archive::text_oarchive outputArchive(outputStream);

	outputArchive << person;
	outputStream.close();
}

void loadPerson(Person& person)
{
	std::ifstream inputStream;
	if(boost::filesystem::exists("myPerson.txt"))
	{
		inputStream.open("myPerson.txt", std::ifstream::in);
		boost::archive::text_iarchive inputArchive(inputStream);
		inputArchive >> person;
	}

	if(inputStream.is_open())
		inputStream.close();
}


In this example I save and load a Person from and to and new object. The corresponding serialization file is:

1
2
22 serialization::archive 17 0 0 4 Test 1 1 0
0 5 Alien


As you can see it captures the polymorphic type "Alien" fine, even when it's a pointer. But you'll also notice I had to register the polymorphic types with the archive beforehand, so the library knew what to do with them.

Also note that unless you want to do some "load_construct data" magic, it's easier to just create each of your classes with a default constructor for boost::serialization to utilize. Otherwise, if the class has no default constructor you basically have to "teach" boost serialization how to construct your object, which adds even more bloat.

Let me know if you have any questions.
Last edited on Jan 18, 2020 at 6:09am
Jan 18, 2020 at 9:01am
Thank you both for your ideas! I think the second one is what I need. The saving of the pointers is not needed so I think I wouldn't want to do that but thanks nonetheless nuderobmonkey.

I am going to get back to you Aaron shortly about your example.
Jan 18, 2020 at 9:23am
Jan 18, 2020 at 6:52pm
Aaron, I sent you a pm.
Jan 19, 2020 at 9:44pm
The saving of the pointers is not needed so I think I wouldn't want to do that but thanks nonetheless nuderobmonkey.

Be sure about this. You can't save anything that contains a pointer directly either, and most containers do. Try it … make a nice vector of 1000 ints, and get the sizeof() it … a binary file write of a vector would lose its data.
Last edited on Jan 19, 2020 at 9:44pm
Jan 19, 2020 at 10:08pm
Jonnin, thanks for the tip. Aaron's post is essentially what I need. Now it's all about understanding it. Would you say even in his example code that the pointers would be saved?
Jan 20, 2020 at 2:28am
The problem is that all the objects are pointers so only the addresses of the objects could be saved but not the objects themselves.


It should be no great revelation that the items pointed to by the pointers are the objects themselves, believe it or not.

Presumably the pointers in question here were created by some form of new statement or similar construction.

A pile of object pointers alone are hardly worth saving - not unlike 'a direction without magnitude'. Somewhere along the line whether boost is used or not some form of dereferencing must take place.

Topic archived. No new replies allowed.