I know I can use:
MyGame game; // the game object
//
ofstream out("mygame.bin", ios::binary);
out.write((char *)&game, sizeof(MyGame));
to save and load the game, but what if I have pointers inside MyGame structure? will the pointers just be saved but not the data it points to?
and: how to solve this?
You can't just write pointers to a stream and expect it to be magically done. You need to implement save/load methods in your objects. E.g:
class Serializable
{
virtual void save(std::ofstream& _out) const = 0;
virtual void load(std::ifstream& _in) = 0;
}; // eo class Serializable
// some game object
class MyObject : public Serializable
{
int myInt;
std::string myString;
virtual void save(std::ofstream& _out) const
{
_out << myInt << myString;
}; // eo save
virtual void load(std::ifstream& _in)
{
_in >> myInt >> myString;
}; // eo load
}; // eo class SomeObject
class MyGame : public Serializable
{
MyObject a;
MyObject b;
virtual void save(std::ofstream& _out) const
{
a.save(_out);
b.save(_out);
}; // eo save
virtual void load(std::ifstream& _in)
{
a.load(_in);
b.load(_in);
}; // eo load
}; // eo class MyGame
Assuming you have not overridden char * cast, yes this will most probably save only pointer and not data.
What you need is Serialization of your object. You can provide a method to marshal the state of object in a bit stream and write that out. And you also need to have method to restore the state back.
You may read more about serialization on wikipedia
Boost has a serialization library, with built in support for deep pointer save and restore, and proper serialization of pointers to shared data.
It's a rather extensive library, but you don't need to write that much code to start using it in your own projects. Well worth the learning effort for anything but the simplest serialization requirements in my opinion.
You could overload the stream out operator (<<) and stream out each individual field (and vice versa)
EDIT: here is a complete example...
#include <iostream>
#include <fstream>
#include <map>
using namespace std;
template <typename T>
void serialize(ostream& str, const T& field)
{
str.rdbuf()->sputn(reinterpret_cast<const char*>(&field), sizeof(T));
}
template <typename T>
void deserialize(istream& str, T& field)
{
str.rdbuf()->sgetn(reinterpret_cast<char*>(&field), sizeof(T));
}
class MyGame
{
public:
MyGame() : a(), b() {}
MyGame(int av, int bv) : a(av), b(bv) {}
friend ostream& operator<<(ostream& str, MyGame const& game);
friend istream& operator>>(istream& str, MyGame& game);
int getA() const { return a; }
int getB() const { return b; }
private:
int a;
int b;
};
ostream& operator<<(ostream& str, MyGame const& game)
{
serialize(str, game.a);
serialize(str, game.b);
return str;
}
istream& operator>>(istream& str, MyGame& game)
{
deserialize(str, game.a);
deserialize(str, game.b);
return str;
}
int main(void)
{
{
ofstream fout("test.bin", ios::binary);
MyGame game(10, 11);
fout << game;
}
{
ifstream fin("test.bin", ios::binary);
MyGame game;
fin >> game;
cout << "game.a: " << game.getA() << ", game.b: " << game.getB() << endl;
}
return 0;
}
You must understand the issues with this approach though, such as the resulting file will be platform specific (i.e. non-portable) etc.
Try game.serialize(out);. In your serialize member function call serialize of your pointer members.
Make a serializing function per type that needs to be persistent.
Call this for each member.
It is actually similar to serializing over network or visualizing for debug-purposes.
boost.serialize can help you.
"Naive" serialization that just dumps the value of pointers is never going to work because, when deserializing, those pointers will be invalid.
The general approach to this kind of problem would go like this:
Have each object that can be serialized in your game implement a serialize() virtual function (I am assuming all such objects will ultimately derive from the same base class).
Have the base class implement a get_serialized_id() public function. This function will use a static auto-incremented variable to generate a unique id for each object instance, but only the first time it is called (subsequent calls will just return the existing value).
Now, when serializing:
Start with a std::map<int, YourBaseClass*>. Add your game object to this map, using the value returned by get_serialized_id() for the key.
While the map contains objects that have not been serialized yet:
Take the first such object.
Serialize its get_serialized_id().
Serialize it by calling its implementation for serialize(). Have it persist its primitive data as usual. For data available through pointers, call get_serialized_id() on each object pointed to and just serialize the number returned from it. Also, add that object to the map.
This will result in a bunch of objects being serialized (in a "random" order) along with each one's "random" id.
When deserializing:
Start with a std::map<int, YourBaseClass*>. Read the first item in your saved file.
For each object pointed to by this first object, you know a unique id (this is what you serialized instead of a pointer). Fetch the item with this id from the saved file and deserialize it.
Recursively do this until all items have been fetched and deserialized from the saved file.
As each item has all its dependences deserialized in step 3 above, instantiate it as an object and add it to the map.
This will enable you to grab a pointer from the map given an item's id, which you can now use to set the pointer members of objects dependent on this item.
When the recursion ends, the last object in the map will be your main "game" object with all its pointers ready to go.
What you did is shallow copy, if you have pointers in your MyGame class, then a deep copy is a MUST!.
I suggest implementing a function or a set of functiions inside MyGame that will take care of saving its own data to a file,and you will only need to call it.
Thanks everyone for the fast and good answers, but a buddy of mine (who is helping me on this) told me we should do it in another way.
I just save the basics of the object and we recreate the rest in a function.
It's a card-game and to save the stack of cards we'll be saving the ID of the card only (not the objects) and just re-initializing each card when we read in the ID from the file.
Related
I copy this program in my book.But i not understand one line in this program.This line is
friend void exchange(class_1 &,class_2 &);
My question is why use & operator in bracket? Please explain.
#include <iostream>
using namespace std;
class class_2;
class class_1{
int valuel;
public:
void indata(int a){valuel=a;}
void display (void){cout<<valuel<<"\n";}
friend void exchange (class_1&, class_2&);
};
class class_2{
int valuel_2;
public:
void indata(int a){valuel_2=a;}
void display (void){cout<<valuel_2<<"\n";}
friend void exchange (class_1&, class_2&);
};
void exchange (class_1 &x,class_2 &y){
int temp=x.valuel;
x.valuel=y.valuel_2;
y.valuel_2=temp;
}
int main()
{
class_1 c1;
class_2 c2;
c1.indata(100);
c2.indata(200);
cout <<"values before exchange"<<"\n";
c1.display();
c2.display();
exchange(c1,c2);
cout <<"values after exchange"<<"\n";
c1.display();
c2.display();
return 0;
}
"&" means that the two arguments are references. See What are the differences between a pointer variable and a reference variable in C++? for more info.
By passing the values by reference, your exchange method will be able to modify the objects in memory. Try without the & and see what happens! Your exchange() method will instead take copies of the object you pass in, but not actually modify the data as it's stored in memory; in other words, the changes that occur when leaving off the & are local to that method, since the method is getting a copy and not the reference to the memory location of that object!
I have a class called Game which contains the following:
vector<shared_ptr<A>> attr; // attributes
D diff; // differences
vector<shared_ptr<C>> change; // change
My question is, how can I write these (save) to a file and read/load it up later?
I thought about using a struct with these in it, and simply saving the struct but I have no idea where to start.
This is my attempt so far, with just trying to save change. I've read up a lot on the issue and my issue (well one of them, anyway) here seems to be that I am storing pointers which after closing the program would be invalid (compounded by the fact that I also free them before exiting).
/* Saves state to file */
void Game::saveGame(string toFile) {
ofstream ofs(toFile, ios::binary);
ofs.write((char *)&this->change, sizeof(C));
/* Free memory code here */
....
exit(0);
};
/* Loads game state from file */
void Game::loadGame(string fromFile) {
ifstream ifs(fromFile, ios::binary);
ifs.read((char *)&this->change, sizeof(C));
this->change.toString(); // display load results
};
Can anyone guide me in the right direction for serializing this data? I'd like to use only standard packages, so no boost.
Thanks.
I have no idea how is implemented classes A, C or D, but that is the first question: how to serialize an object of that class. For the C case, you need to implement something like this:
std::ostream& operator <<(std::ostream& os, const C& c) {
// ... code to serialize c to an output stream
return os;
}
std::istream& operator >>(std::istream& is, C& c) {
// ... code to populate c contents from the input stream
return is;
}
or, if you prefer, create a write() and read() function for that class.
Well, if you want to serialize a vector<shared_ptr<C>> looks obvious you don't want to serialize the pointer, but the contents. So you need to dereference each of those pointers and serialize. If the size of the vector is not known before loading it (i.e., is not always the same), you'll need to store that information. Then, you can create a pair of functions to serialize the complete vector:
std::ostream& operator <<(std::ostream& os, const std::vector<std::shared_ptr<C>>& vc) {
// serialize the size of the vector using << operator
// for each element of the vector, let it be called 'pc'
os << *pc << std::endl; // store the element pointed by the pointer, not the pointer.
return os;
}
std::istream& operator >>(std::istream& is, std::vector<std::shared_ptr<C>>& c) {
// read the size of the vector using >> operator
// set the size of the vector
// for each i < sizeo of the vector, let 'auto &pc = vc[i]' be a reference to the i-th element of the vector
C c; // temporary object
is >> c; // read the object stored in the stream
pc = std::make_shared<C>(c); // construct the shared pointer, assuming the class C has copy constructor
return is;
}
And then,
/* Saves state to file */
void Game::saveGame(string toFile) {
ofstream ofs(toFile);
ofs << change;
....
};
/* Loads game state from file */
void Game::loadGame(string fromFile) {
ifstream ifs(fromFile);
ifs >> change;
};
I know there are a lot of things you still need to resolve. I suggest you to investigate to resolve them so you understand well how to solve your problem.
Not only are you saving pointers, you're trying to save a shared_ptr but using the wrong size.
You need to write serialization functions for all your classes, taking care to never just write the raw bits of a non-POD type. It's safest to always implement member-by-member serialization for everything, because you never know what the future will bring.
Then handling collections of them is just a matter of also storing how many there are.
Example for the Cs:
void Game::save(ofstream& stream, const C& data)
{
// Save data as appropriate...
}
void Game::saveGame(string toFile) {
ofstream ofs(toFile, ios::binary);
ofs.write((char *)change.size(), sizeof(change.size());
for (vector<shared_ptr<C>>::const_iterator c = change.begin(); c != change.end(); ++c)
{
save(ofs, **c);
}
};
shared_ptr<C> Game::loadC(ofstream& stream)
{
shared_ptr<C> data(new C);
// load the object...
return data;
}
void Game::loadGame(string fromFile) {
change.clear();
size_t count = 0;
ifstream ifs(fromFile, ios::binary);
ifs.read((char *)&count, sizeof(count));
change.reserve(count);
for (int i = 0; i < count; ++i)
{
change.push_back(loadC(ifs));
}
};
All the error handling is missing of course - you would need to add that.
It's actually a good idea to at least start with text storage (using << and >>) instead of binary. It's easier to find bugs, or mess around with the saved state, when you can just edit it in a text editor.
Writing your own serialization is quite a challenge. Even if you do not use boost serializatoin I would recommend you learn how to use it and comprehend how it works rather than discovering it yourself.
When serializing you finally end up with a buffer of data of which content you have very vague idea. You have to save everything you need to be able to restore it. You read it chunk by chunk. Example (not compiled, not tested and not stylish ):
void save(ostream& out, const string& s)
{
out << s.size();
out.write(s.c_str(), s.size());
}
void load(istream& in, string& s)
{
unsigned len;
in >> len;
s.resize(len);
in.read((char*)s, len);
}
struct Game
{
void save(ostream& out)
{
player.save(out);
};
void load(istream& in)
{
player.load(in);
}
};
struct Player
{
void save(ostream& out)
{
// save in the same order as loading, serializing everything you need to read it back
save(out, name);
save(out, experience);
}
void load(istream& in)
{
load(in, name);
load(in, experience); //
}
};
I do not know why you would do it to yourself instead of using boost but those are some of the cases you should consider:
- type - you must figure out a way to know what "type of change" you actually have there.
- a string (vector, whatever) - size + data (then the first thing you read back from the string is the length, you resize it and copy the "length" number of characters)
- a pointer - save the data pointed by pointer, then upon deserialization you have to allocate it, construct it (usually default construct) and read back the data and reset the members to their respective values. Note: you have to avoid memory leakage.
- polymorphic pointer - ouch you have to know what type the pointer actually points to, you have to construct the derived type, save the values of the derived type... so you have to save type information
- null pointer... you have to distinguish null pointer so you know that you do not need to further read data from the stream.
- versioning - you have to be able to read a data after you added/removed a field
There is too much of it for you to get a complete answer.
What would be the elegant and simple way (if exists) to implement a storage of generic objects (all other objects inherit from base). Once the object is stored, use a string handle to retrieve object or copy into another.
class Object{
public:
Object(){};
~Object(){};
};
class ObjectHandler{
public:
ObjectHandler(){};
~ObjectHandler(){};
void InsertObject(std::string handle, std::shared_ptr<Object> obj){
// some things happen before inserting
_obj.insert(std::make_pair(handle,obj));
}
std::shared_ptr<Object> RetrieveObject(std::string handle){
// some things happen before retrieving
return _obj[handle];
}
private:
std::map<std::string,std::shared_ptr<Object>> _obj;
}
For example, user defined classes are
class Dog : public Object{
public:
Dog(){};
Dog(std::string name){dogName=name};
~Dog(){};
std::string dogName;
//...
}
class Cat : public Object{
public:
Cat(){};
Cat(std::string name){catName=name};
~Cat(){};
std::string catName;
//...
}
And the following code is executed
void main(){
ObjectHandler oh;
Cat c("kitten"), cc;
Dog d("doggy"), dd;
oh.InsertObject("cat#1",c);
oh.InsertObject("dog#1",d);
cc = oh.RetrieveObject("cat#1");
dd = oh.RetrieveObject("dog#1");
std::cout << cc.catName << std::endl; // expect to print 'kitten'
std::cout << dd.dogName << std::endl; // expect to print 'doggy'
}
I believe there should be some well established idea (pattern) to make this working right.
I also suspect std::shared_ptr might be useful here.
Thanks,
I would exercise caution here, in your example you're storing your objects as Object strictly (on the stack), since that would only allocate enough space for something of type Object, should you insert something that inherits from the type, it would have the part that describes the subclass sliced.
Good examples of the problem at hand:
http://www.geeksforgeeks.org/object-slicing-in-c/
What is object slicing?
One way to get around the problem is to handle pointers to the objects in your ObjectHandler instead, the objects themselves allocted on the heap.
If I'm just misinterpreting your post, then I apologise.
But if you as you said, will store smart pointers to the object instead, making a pair should look something like this:
std::map<std::string,std::shared_ptr<Object>> _obj;;
std::string handle = "hello"; //Or whatever the actual handle is.
std::shared_ptr<Object> keyvalue(new Object());
objects[handle] = std::shared_ptr<Object>(keyvalue); //alternative to std::make_pair
objects.insert(std::make_pair(handle, std::shared_ptr<Object>(keyvalue))); //this also works
Depending on at what point you want to start handling objects with smart pointers, insertion might look like:
void InsertObject(std::string handle, Object* obj){
_obj.insert(std::make_pair(handle,std::shared_ptr<Object>(obj)));
}
std::string key("hi");
InsertObject(key, new Object());
or alternatively just:
void InsertObject(std::string handle, std::shared_ptr<Object> obj){
_obj.insert(std::make_pair(handle, obj));
}
Also note that std::map's indexing operator[] overwrites the old value if it exists, while the insert you're already using will only insert if the old one doesn't exist.
Problem:
Error: The "&" operator can only be applied to a variable or other l-value.
What I've tried:
dynamic_cast<char*>(e)
reinterpret_cast<char*>(e)
static_cast<char*>(e)
(char*) e
What I'm trying to do:
Write the array e.data (private) to a binary file.
Notes:
e.getSize() returns number of elements in array
e[] returns Employee object.
Code:
fstream fout;
fout.open(filename.c_str(), ios::out|ios::binary);
if(fout.good())
{
for(int i=0;i<e.getSize();i++)
{
fout.write((char*)&e[i], sizeof(e[i]));
}
}
fout.close();
Employee.h
class Employee {
friend std::ostream& operator<<(std::ostream&, const Employee &);
private:
int id;
char name[50];
char address[100];
char phone[20];
char department[100];
int salary;
public:
Employee();
~Employee();
Employee(int,char[],char[],char[],char[],int);
bool operator==(Employee&);
};
I'm lost at what to do, from what I remember fout.write((char*)&e[i], sizeof(e[i])); is how to write to binary files.
Edit:
e is declared like so:
MYLIB::Bucket<Employee> e;
template <class T>
class Bucket {
private:
T* bkt;
int size;
int capacity;
static const int stepsize = 10;
public:
Bucket();
~Bucket();
void push_back(const T&);
T operator[](int);
int getSize();
int getCapacity();
};
Edit 2:
fout.write(reinterpret_cast<char*>(e[i]), sizeof(e[i])); gives me line 122: Error: Using reinterpret_cast to convert from ? to char* not allowed. (Line 122 is the line just quoted)
Edit 3:
tempemp = e[i];
fout.write((char*)(&tempemp), sizeof(e[i]));
Compiles but gives a segmentation fault, I'll investigate why.
Compiles, the segmentation fault looks unrelated.
MYLIB::Bucket<Employee> e;
this seems to be a container. e[i] gives you an Employee by value. you need to get this object's address using &e[i] but you can'd do that since it's an r-value so you need to copy it to a non r-value:
Employee copye = e[i];
fout.write((char*)©e, sizeof(e[i]));
Should work.
On a side note, this all looks like terrible code and I don't envy whoever needs to maintain or read it. A few points:
you should not be using the binary format of the in-memory object as your serialization format. Use a proper serialization format like protobuf or xml or json
Why pull your own strange containers when you can use std::vector std::list ? re-inventing the wheel is always bad
returning an element by value from a container creates copies which degrade performance.
I think that
T operator[](int);
is returning a temporary object that must be bound to something before an address can be taken
const Employee& emp = e[i];
fout.write((char*)&emp, sizeof(emp));
might work, assuming this answer is correct that taking a reference extends the life of a temporary object
An alternative might be to return a reference to the object, which would remove the creation of temporary objects
const T& operator[](int);
I'm quite new to C++ and I am trying to store objects inside a std::vector like this:
Event.h:
//event.h
class Event
{
public:
Event();
Event(std::string name);
~Event();
void addVisitor(Visitor visitor);
private:
std::vector<Visitor> m_visitors;
};
Event.cpp:
//event.cpp
Event::Event() :
m_name("Unnamed Event")
{
}
Event::Event(std::string name) :
m_name(name)
{
}
void Event::addVisitor(Visitor visitor)
{
this->m_visitors.push_back(visitor);
}
void Event::listVisitors()
{
std::vector<Visitor>::iterator it;
for(it = this->m_visitors.begin();it != this->m_visitors.end(); ++it)
{
std::cout << it->getName() << std::endl;
}
}
Visitor.h:
//visitor.h
class Visitor
{
public:
Visitor();
Visitor(std::string name);
~Visitor();
std::string getName() const;
void listVisitors();
private:
std::string m_name;
};
Visitor.cpp:
//visitor.cpp
Visitor::Visitor() :
m_name("John Doe")
{
}
Visitor::Visitor(std::string name) :
m_name(name)
{
}
std::string Visitor::getName() const
{
return m_name;
}
main.cpp:
//main.cpp
int main()
{
Event *e1 = new Event("Whatever");
Visitor *v1 = new Visitor("Dummy1");
Visitor *v2 = new Visitor("Dummy2");
e1->addVisitor(*v1);
e1->addVisitor(*v2);
}
If I do it like this I would have to add a copy constructor which would make a deep copy so the object gets copied properly into the vector. I'm looking for a way around it by only storing pointers to the objects in a vector.
I already tried it with std::vector<std::unique_ptr<Visitor> > m_visitors, but then I got some errors when calling addVisitor in main.cpp. Of course I changed the declaration of the class members accordingly.
How would an appropriate declaration of the members and the member function look like to make it work?
Stylistically, if you are passing pointers, just accept pointers as the function arguments.
What's happening in the example code above is that the visitors are getting copied to become function arguments and the pointers you had are unreferenced by anything outside of the main function.
I can't speak to what the errors are that you're seeing as you didn't describe them but it probably has to do with incompatible types.
Just get rid of the news because for these data structures they're unnecessary.
int main()
{
Event e1("Whatever");
Visitor v1("Dummy1");
Visitor v2("Dummy2");
e1.addVisitor(v1);
e1.addVisitor(v2);
}
I would suggest that if you don't know how to use pointers you couldn't possibly want to store them instead (they're a hassle IMO to store in the vector when copying by value works just fine).
The compiler generated copy constructor should work just fine.
No manual deep copy required, because you are quite correctly using std::string, which supports RAII.
However, your main function has three memory leaks — there is no need to use new there anyway, so simply don't.
General rule of thumb:
If, at any time T, you're thinking of introducing more pointers into your code, then you're probably going in the wrong direction.