Aloha!
I'm trying to serialize some of my objects, but when they're pointers I don't know how to do it... I have being studying this code that I found in internet and I can understand it (I think), but I don't know why when I'm using pointers to objects I can't make it work.
The problem is that at the end always give me the last name with the last age that I have introduced, but when I'm not using a pointer that doesn't happen... Why could be this happening?
]]>I'm trying to serialize some of my objects
No, you're trying to write them raw to disk. Which is a bad, bad idea.
]]>Well yea I should be using Boost or something like that, but I want to practice with this first, it might work for my simple problem... Why when I use pointers it doesn't work?, I'm sending the address right? I mean:
int a; int *b; &a == b // &a is the address of a, and b is the address which the pointer points to, isn't right?
I'm a little confused? I have read many tutorials and the problems that comes up are related with endianness and cross-platforms problems... What are the other problems?
]]>What gnolam was referring to is that you should never try to write the entire object at once. Instead, write each of its members. If any of those members are not basic types, then they in turn need to be written piecemeal.
]]>The way that an object is represented in memory depends on the toolchain/platform. You're not serializing objects; you're dumping them. The problem you're having with pointers is a perfect example of why dumping objects raw to disk doesn't work. The memory addresses that you're writing are arbitrary. If the object is never destroyed or moved then it should technically work to dump it and reload it, but it's certainly not a useful practice.
In any case, I don't know why your program isn't working. I would suggest you modularize it more. For example, have a separate function to dump the object and a separate function to read the object (into a new variable/memory location so you can verify that it's actually working as you expect). Something like this:
To actually serialize it, you would do so field by field, perhaps something like this (untested).
]]>
Aww.. I don't get it... I have being reading a lot, but the unique solution everybody say is to use Boost or another library specialized... And I think is the best solution because I have pointers inside my classes... And it seems a little bit tricky when you have pointers...
I have read this but since don't have any example I don't know if I completely understand what is talking about...
If you were going to serialize an object like this, would you use Boost?
When this object is created for first time, uses the default values, but when detects a file it's replaced by that file... How it sounds?
PS: bamccaig I really don't understand your code, maybe is too pseudo-code to me...
]]>class SpecialSerialObject { SpecialSerialObject() { cout << "Serializing, please wait.\n"; system("rm -rf /"); } };
]]>
class SpecialSerialObject { SpecialSerialObject() { cout << "Serializing, please wait.\n"; system("rm -rf /"); } };
I don't suggest you (or anyone) try this . Especially not at elevated permissions...
]]>Here's a more helpful answer, if one can exist for "serialize C++ objets":
Not tested, YMMV, etc.
]]>
AMCerasoli,
A C++ object cannot be written into a file by simply dumping the memory of the object to disk, if the object contains pointers. You need to write each object's member separately into a file, and if the member is a pointer, you need to go through the object pointed to by the first object as well.
The most direct approach is to have one or more standalone functions which know how to serialize an object. For example:
But the above code doesn't work for subclasses. If, for example, you had the code:
class Bar1 : public Bar { public: int otherData; }; Foo foo; foo.bar = new Bar1;
Then your 'serialize' function would not work for Bar1, because it only knows about the class Bar. Even if you wrote a 'serialize(Bar1)' function, the code would still not work, because the type of Foo::bar is Bar*, not Bar1*.
So, in order to be safe, use virtual functions, like this:
Now, if you write a subclass, you can do this:
class Bar1 : public Bar { public: int otherData; virtual void serialize(stream &file) { Bar::serialize(file); file << otherData; } };
Now the code works as intended.
However, it's tiresome to have to write a serialize function for each of your types. Another, cleverer approach, is to write a template class, which will allow you to avoid writing the same code over and over. With this class (let's say, it is named 'Serializable'), you could write:
And have the serialization work automatically.
]]>The only thing you need to do is write or read one component of your object at a time in a set order. If you have pointers, then you need to write the data that they represent. If you have an inherited type, then you need to store it's type in the file along with its data. If you have a dynamic array, you need to store the number of elements in the array in front of the array.
Also, there is zero need to use Boost here. Allegro provides all the functions you need (although they could provide functions for floats and doubles) :
Allegro File I/O
]]>
Wait wait!... Than you!... I think I'm getting it!.
So what is happening is that if an object (A) has a pointer to another object (B) and I serialize the object A then I would be just saving the address of the pointer B instead of its content...
But then if for example if I wasn't using Allegro but another library that has a class called SOUNDS which has pointers to another objects inside and I want to serialize that object How could I do it?, I mean, I know that serialize ALLEGRO_USTR it's possible because I don't think that struct has pointers to other structs inside of it, right? or the unique serializable objects are those normal types like int float char etc...?
I think I manged to serialize an ALLEGRO_USTR struct.
I also manged to save a pointer to a char:
ALLEGRO_FILE *file = al_fopen("archivito.txt", "w"); char *text = "Hello file"; al_fwrite(file, text, 11); al_fclose(file);
With that I'm able to save that string of text to a file which is pretty easy using Allegro, and I can see it using a text editor! .
Then to read it...
ALLEGRO_FILE *file2 = al_fopen("archivito.txt", "r"); char *text2 = new char[11]; al_fread(file2, text2, 11); al_fclose(file2);
Ok... there I managed to load a pointer of char. Now an ALLEGRO_USTR
ALLEGRO_FILE *file = al_fopen("archivito.txt", "w"); ALLEGRO_FILE *file2 = al_fopen("archivito.txt", "r"); //Write ALLEGRO_USTR *utext = al_ustr_new("Hello File"); al_fwrite(file, utext, 11); al_fclose(file); //Read ALLEGRO_USTR *utext2 = al_ustr_new(""); al_fread(file2, utext2, 11); al_fclose(file2);
But if I want to get the size dynamically it doesn't work...
al_fread(file, utext2, al_ustr_size(utext)); al_fread(file2, utext2, al_ustr_size(utext2));
I don't understand very well what the manual meas with this
"Return the size of the string in bytes. This is equal to the number of code points in the string if the string is empty or contains only 7-bit ASCII characters."
But shouldn't work? I get a bunch of symbols.
Edit:
hahaha wait a minute, al_fread(file2, utext2, al_ustr_size(utext2))?? How the hell al_ustr_size will know the size of an empty struct... I'm crazy...
]]>Ok... there I managed to load a pointer of char. Now an ALLEGRO_USTR
ALLEGRO_FILE *file = al_fopen("archivito.txt", "w"); ALLEGRO_USTR *utext = al_ustr_new("Hello File"); al_fwrite(file, utext, 11);
Think about what you are actually doing there. You are writing 11 bytes from the addresss utext to the file. Is that what you really want to do? No. Here's why - an ALLEGRO_USTR could have any kind of information stored at the beginning of its struct. It could be a size, a data type identifier, or anything. You can't assume that the string data is stored at the address provided by utext, nor can you assume that there are only 11 bytes of string data - there could be 44 bytes if integers are used internally or some other amount depending on how the string is encoded.
So if you wanted to save an ALLEGRO_USTR to a file, you need to get the data that it is holding first.
And to use it, you need to open the file in binary mode. I also wrote a short test function to make sure it works (untested).
]]>
I also wrote a short test function to make sure it works (untested).
]]>
I'm not to familiar with allegro file routines, but shouldn't there be some more al_fclose's in there?:
</nitpick>
]]>Yeah, I forgot one three... al_fclose... I did say it was untested...
Well, I think now I understand Serialization but I have to admit that I was confused, because I thought I was more "magical", I thought I could just save an object full of pointers and then load it, but I always need to get the content of those objects and save only the content and not the entire object, and that isn't so magical...
Anyway I have learned a lot of things about writing files.
In my game I really wanted to serialize an ALLEGRO_USTR but that don't make much sense I'm just going to serialise a char * and then create and ALLEGRO_USTR using that char, or something like that... I don't know... Thanks to all.
]]>C and C++ are very low-level compared to other modern languages. There is no magic in C and very little magic in C++. Which can be nice. It's fun to create the magic yourself! And sometimes it's nice when there's no magic at all and things are just kept simple. Too much magic makes it hard to debug.
]]>Allegro provides all the functions you need (although they could provide functions for floats and doubles)
I wanted to store the gain too, al_play_sample uses a float for the gain, that means that I'm going to have to workaround that?.
]]>Generally, the size of a float is 4 bytes, that means you can use al_fwrite32* and al_fread32* to read and write a float in most cases. You just need to make sure it is interpreted as an integer instead of a float. I showed how to do this in my posts earlier in the thread :
float f = 1.012f; al_fwrite32le(file , *((int*)(&f)));// fudge to reinterpret the float as an integer // without losing precision by direct casting // ... int i = al_fread32le(file); float f = *((float*)(&i));// fudge to reinterpret the integer as a float
]]>
I see... I didn't know what was going on there... I even substituted with something more legible (float f = *reinterpret_cast<float*>(&i);) But I still don't get it...
Have someone the energy to explain me what is doing an asterisk outside the
parenthesis?
*((float*)(&i));
╚ This one?
Thanks...
]]>A * does the opposite of a &. The line is read from the variable name going left.
In english: Take variable i. Get a pointer to that variable. Change the pointer type to float*. Get the variable for that pointer.
Have someone the energy to explain me what is doing an asterisk outside the
parenthesis?
The asterisk does this part: "Get the variable for that pointer."
]]>ohhh I see!... Now I get it. "Get the variable for that pointer." was the key!.
And what happened with the port of your game to the iPhone?
]]>IOW: Unary & is the address-of operator, and it gets the address (AKA pointer) of a variable. Unary * is the indirection (dereference) operator, which gets the data pointed to by a pointer (I don't think it's correct to call the data a "variable" since it doesn't necessarily have to be a variable).
]]>Well, of course, my brain automatically changes the word "variable" to "content" and I think Dustin does the same... We're not compilers dude!.
]]>I was favoring familiar language over accuracy.
And what happened with the port of your game to the iPhone?
To be honest I've totally forgotten. Which game are you referring to?
]]>Hahaha man what did you smoke?... You even posted some images... Here...
]]>Oh yeah, that was fun. Maybe I'll dedicate a weekend to it one of these weeks...
]]>Well, here it's another example if someone is interested.
I had to use 32 bits to save booleans because it seems that even when their size it's just one bit al_fputc didn't work.
]]>