I have a C++ objects that looks like this
class myClass
{
vector<OtherClass*> otherClassVector;
AnotherClass* anotherClassObj;
// A few other primitive types and functions
}
What is the best way to store this to disk and read it back programmatically?
Will using fstream read/write in binary mode work? Or should I use boost serialization? And why?
I don't require the stored file to be human readable.
Using boost::serialization is simply, than write your own serializer. If OtherClass is concrete type (not base) - serialize by read/write is simple, for vector - just save size and than array (if your myClass has no non-POD types) and then store element on which points anotheClassObj pointer...
You can serialize objects with ofstream f("filename", std::ios::binary); only if those objects are POD-types.
Anything else needs to be handled manually. For a simple example, if the object contains any pointers, the addresses of those will be saved, not the data they point at.
For more complex types, you will have to either serialize them completely manually (write a class or function that will save all the POD data from the class and do something tricky with all the "special" data)), or use boost serialization.
The C++_Middleware Writer may be of interest. It has some advantages over other approaches.
Related
I have a base class and two derived classes. I want to write and read objects of these classes to / from a file. I was thinking about virtual functions to write/read data, but I don't know where should I place these functions. In the base class? When I will be reading data from the file I will store pointers to objects in a vector, but I suppose I cannot have a vector of pointers to objects of a class in which this vector is declared. Could someone help me solve this problem? Thanks in advance for any advice.
When you write the objects to the file, you also have to store some information such that you know the type/class of the object when reading it in again later; Otherwise you will not know which of the derived classes to instantiate.
Once you have solved this, you can decide to store the objects where ever and in which way you want.
As far as I understand your problem, you have a base class and two derived classes. All of them you want to write and read from a file and you want to read more than one instance from this object at a time.
In my opinion you need a container class, which takes care of the reading and writing. This means you implement a class, which stores your instances in a vector and then can save them to the disk and read them again.
Saving different types of classes, which are inherited from the same base class, requires additionally that you add a type, which you have to check during the writing and the reading, to process the stored information correctly.
Is there an easy way to write a class that contains other classes and arrays of pointers to other classes to a file?
Thanks!
What you want to look into is called serialisation. It's the method of turning an object into a stream of bytes. The opposite is called deserialisation and is for constructing an object from a stream of bytes.
There is no way to do this automatically in vanilla C++, and if you would just write the object to file just like that from the object's address and size you would not write all that it points to.
Serialisation can be done in a lot of ways, either manually or using a third party library. I personally really like the Cereal library for serialisation/deserialisation in C++. Link here
I have a vector with pointers to a base class object so i can manage objects derived from that class.
vector <Product*> products;
i am trying to write these objects to a file while iterating through the vector
but i am not sure if this works correctly.
void Inventory :: saveProductsToFile()
{
ofstream outfile;
outfile.open("inventory.dat",ios::binary);
list <Product*> :: iterator it;
for(it=products.begin(); it!=products.end(); it++)
outfile.write((char*)*(it),sizeof(Product));
}
The file is created but i have no idea if i'm saving the actual objects themselves or their
addresses.Is this correct or is there another way?
This is how the file looks like:
ˆFG " H*c \Âõ(œ##pFG h*c b'v b#
You code can work. You cannot serialize polymorphic objects in
that way. For starters, you're writing the hidden vptr out
to disk; when you reread the data, it will not be valid. And
you're only writing out the data in the base class (Product),
because that's what sizeof(Product) evaluates to. And
finally, just writing a byte image of anything but a char[]
will probably mean that you won't be able to reread the data
some time in the future (after a compiler upgrade, or a machine
upgrade, or whatever).
What you have to do is to define a format (binary or text) for
the file, and write that. For the basic types, you can start
with something existing, like XDR or Protocol buffers, but
neither of these work that well with polymorphic types. For
polymorphic types, you have to start by defining how you
identify the type in question when rereading. This can be
tricky: there's nothing in std::type_info which helps, so you
need some means of establishing a relationship between your
(derived) types and the identifier. Then every derived class
must implement a write function, which first writes its type,
then writes its data out, one element by one. When reading, you
read the type, look up the appropriate read function for that
type in a map, and call that function, which then reads the data
one by one.
Finally, I might point out that all successful serialization
schemes I've seen depend on generated code. You describe your
types in a separate file, or in special markup (in a specially
marked comment in the C++), and have a program which reads that,
and generates the necessary code (and often the actual classes
you use).
Thats not how you "serialize" data. Like this the pointers are only valid during runtime or until you delete them (depending on what happens/stops first). Like this you wouldn't be able to restore your data, because after the program has stopped everything from your former memory becomes invalid. You would have to store the actual values from your class.
I have a bunch of structs in C++. I'd like to save it to file and load them up again. Problem is a few of my structs are pointers to base classes(/structs). So i'd need a way to figure out the type and create it. They really are just POD, they all have public members and no constructors.
What is the easiest way to save and load them from file? I have a LOT of structs and the only types i use are ints, pointers or c strings. I am thinking i could do some macro hacks. But really i have no idea what i should do.
Have you tried the Boost serialization library?
Don't roll your own here - use something well-developed and tested. One idea is Protocol Buffers
The pointers pose a specific issue: I suppose that multiple struct may actually refer to the same pointer and that you'd like a single pointer to be recreated when deserializing...
The first idea, to avoid boiler-plate code, is to create a compile-time reflexion tool:
BOOST_FUSION_ADAPT_STRUCT
BOOST_FUSION_ADAPT_STRUCT_NAMED
Those 2 macros will generate some wicked information on your struct so that you can then use them with Fusion algorithms, which cross the gap between compile-time and run-time.
Now, you need something that will be able to serialize and deserialize your data. Deserialization is usually a bit more difficult, though here you have the advantage of no polymorphism (which always makes things difficult).
Normally, on a first pass you identify the graph of objects to serialize, assign them all an ID, and use this ID in lieu of the pointer when serializing. For deserializing, you use a 3-columns map:
the map is ID -> (pointer to allocated object, list of pointers that could not be set)
allocate all objects, filling the ID map with a pointer to the allocated object each time
when you need to deserialize an ID, look it up in the map, if absent put a pointer to your pointer in the corresponding list
when you put the pointer to the allocated object in the map, take the time to fill all 'not set' pointers (and remove the list at the same time)
Of course, it's better to have frameworks handling it for you. You may try out s11n, if I remember correctly it handles cycles of references.
I have serialized a C++ object and I wish to allocate space for it, although I can't use the "new" operator, because I do not know the object's class. I tried using malloc(sizeof(object)), although trying to typecast the pointer to the type the serialized object is of, the program shut down. Where is the information about the object class stored?
class object
{
public:
virtual void somefunc();
int someint;
};
class objectchild:public object
{
}
object *o=(object*)malloc(sizeof(objectchild));
cout << int(dynamic_cast<objectchild*>(o)) << endl;
This causes a program shutdown.
Thank you in advance.
I have serialized a C++ object
I'm not sure you have. If you've written anything like this:
object *p = new objectchild();
some_file.write((char*)p, sizeof(objectchild));
then you haven't serialized your object. You've written some data to file, and (in most implementations) that data includes a pointer to a vtable and type information. When you "deserialize" the data, on another machine or in another run of the same program, the vtable will not in general be at the same address, and the pointer is useless.
The only way to serialize an object in C++ is to write its data members, in a known format you design. That known format should include enough information to work out the type of the object. There are frameworks that can help you with this, but unlike Java there is no mechanism built into the language or standard libraries.
you should not mix C++ and C memory routes. dynamic_cast checks actual type of object. in your case you have raw memory casted to object *
Rewrite your code so that you can read the type of the object in some way from your serialized archive. You can do this by string or by some custom values you use, but it probably won't be generic.
For example, if you are writing a CFoo object, first stream the value "1". If you are writing a CBar, stream the value "2 .
Then, when reading back the archive, if you see a "1" you know you have to "new" a CFoo, and if you read a "2" you know you have to new a CBar.
Alternatively, you could use a full-featured serialization library (or use it as inspiration).
See for example boost::serialization
You need the following code
object *o = new objectchild;
to use dynamic_cast.
You're trying to dynamic_cast a memory location with nothing in it. malloc has given you free space to place an object, but until the new() operator is called no object is there, so when dynamic_cast does it's type-safety check it will fail. You could try using static_cast rather than dynamic_cast, since static doesn't do a type-safety check, but really you shouldn't mix C and C++ allocation/casting styles like that.