Re: serialize args
On May 18, 5:34 pm, "RB" <NoMail@NoSpam> wrote:
Thanks again, and if you have time could you comment on the
items below. I would much appreciate it.
---
( The below happens same with both fetching my own CArchive&
arg for Serialize and also letting CDocument's OnFileSave fetch it=
)
I checked the file contents in a hex viewer after each method.
on the first method of using the CMapStringToString.Serialize
as with
ExpMap1.Serialize(ar) //ExpMap1 is a CMapStringToString object.
it writes to file:
03 00 01 61 03 57 57 57 01 62 04 5A 5A 5A 5A 01 a WWW b ZZZZ
63 04 44 44 44 44 =
c DDDD
-------------------------
(Note the a, b, and c are Key values, the capital letters are the
map associated stored values)
-------------------------
And then if I write the file using the
CMapStringToString* pExpMap1;
pExpMap1 = &ExpMap1;
if (ar.IsStoring())
{
ar << pExpMap1;
it writes to file:
FF FF 00 00 12 00 43 4D 61 70 53 74 72 69 6E 67 CMapString
54 6F 53 74 72 69 6E 67 03 00 01 61 03 57 57 57 ToString a WWW
01 62 04 5A 5A 5A 5A 01 63 04 44 44 44 44 b ZZZZ c DD=
DD
-------Obviously prepending
FF FF 00 00 12 00 43 4D 61 70 53 74 72 69 6E 67 CMapString
54 6F 53 74 72 69 6E 67 =
ToString
to the file with this method.
So this is interesting, makes me wonder if my previous method
leaving this added data out is not good ??? I surmise the important th=
ing is
"read" it the method I "write" it.
Yeah, that's not. In the first snippet, you call Serialize(ar) on your
map object. In the other, you use operators >> / << with a pointer to
an object. These are two completely different beasts and you can't mix
them.
First simply calls Serialize on an object; that's all: call Serialize
function.
Second inserts/extracts the object in/from the archive; that's much
more involved:
First off, note that operator >> puts something in a pointer. So if
you get the pointer, where is the object? Answer is: object was
created on the heap and once you received it through operator>>, it's
in your hands (that is, you should call delete on it eventually).
Corolary: doing "ar << &myMemberObject" on store and "ar >> pointer"
on load is normally an error (you made that mistake). It should be
either myMemberObject.Serialize(ar), either ar << pMyMemberObject / ar
pMyMemberObject on store / load.
Second, imagine this (simple derivation):
class C1 {...}
class C2: public C1 {}
class Doc
{
C1* p;
void f1() { p = new C1; }
void f2() { p = new C2; }
void Serialize()
{
// store
ar << p;
// load
ar >> p;
}
}
Now, hang on! Imagine I called f1(), then saved. When I load, I should
get an instance of C1 in p; if, however, I called f2 prior to storing,
I should get an instance of C2. WTF!? Well, operators >> and << work
together to give you correct object type on load. That's their second
part of functionality.
And third... Imagine this:
class C1 {}
class C2
{
C1* p;
void Serialize() { ar << p / ar >> p; }
}
class C3
{
C1* p;
void Serialize() { ar << p / ar >> p; }
}
class Doc
{
C2 c2;
C3 c3;
void f()
{
c2.p = new C1;
c3.p = c2.p;
}
void Serialize()
{ c1.Serialize(); c2.Serialize() }
}
Now, hang on again! c2 and c3 in the document were "sharing" a pointer
to C1. In serialization, each of them put SAME C1* into the archive.
When I load, I sure would like to have SAME pointer again in Doc.c2.p
and Doc.c3.p. Well, that's the third functionality of operator>> / <<.
It allows you to serialize arbitrarily complex graph of objects,
provided that "shared" objects are on heap.
To summarize: obj.Serialize(ar) and ar << pObj / ar >> pObj serve
different purposes and can't be mixed.
it's a good idea to put SerializeClass(RUNTIME_CLASS(CMyDoc))
at the top of Serialize for the document and so one can use
GetObjectSchema in a "standard" manner,
I cannot seem to get the correct implementation in my
// allow the base class to serialize along
// with its version information
ar.SerializeClass(RUNTIME_CLASS(CDocument));
CDocument::Serialize(ar);
because this brings up an
"unsupported operation was attempted" when called
from my ar fetch and a
"failed to save document" when called thru
the OnSaveDocument
??? I should have CDocument as the base class correct ?
No, no, you missed a big chunk of my post here above.
Indeed, SerializeClass is documented by MSDN as a way to serialize
base class's class information. In more detail, SerializeClass
serializes class identifier (It's not simply class name, it's a bit
more involved than that) and schema number. So this is convenient when
you have this situation:
class C1 {...}
class C2 : public C1 {...}
Suppose that you define schema for C1 and C2, and each schema evolves
separately. Typically, your Serialize functions end up being like
this:
(supposing that m2, m3, m4 were added to serialization in "schemas" 2,
3, 4 of class C1)
void C1::Serialize()
{
//store:
ar << m1 << m2 << m3 << m4;
//load:
UINT nSchema = ar.GetObjectSchema();
ar >> m1;
if (nSchema>=2)
ar >> m2;
if (nSchema>=3)
ar >> m3;
if (nSchema>=4)
ar >> m4;
}
(supposing that mC22, mC23 were added to serialization in "schemas" 2,
3 of class C2)
void C2::Serialize()
{
SerializeClass(RUNTIME_CLASS(C1));
C1::Serialize(ar);
//store:
ar << mC21 << mC22 << mC23;
//load:
UINT nSchema = ar.GetObjectSchema();
ar >> mC21;
if (nSchema>=2)
ar >> mC22;
if (nSchema>=3)
ar >> mC23;
}
So SerializeClass allows you to have a long inheritance chain, and
when you serialize and object of the most derived class, you can
correctly serialize it's data and data of any of it's base classes,
even though schema of each class evolved separately.
BUT!!! (BIG BUT HERE). With a document, and in other cases, where you
don't use operator>> / << with a pointer to an object, but you still
want to use class/schema information, you can use SerializeClass to
serialize class/schema information for the class you're serializing,
NOT the base class. So to have that schema number for your doc, you
do:
void CMyDoc::Serialize(ar)
{
SerializeClass(RUNTIME_CLASS(CMyDoc)); // NOT CDocument!
// Or SerializeClass(GetRuntimeClass());
BusinessAsUsual(ar);
}
Goran.