From: Goran on
On Jun 5, 4:14 pm, "RB" <NoMail(a)NoSpam> wrote:
> >........................ For example, the MFC framework (which was compiled
> > in 2008 without knowing your class name) creates your CFileHandlingDoc at startup, but it does so without a
> > CFileHandlingDoc* p =  new CFileHandlingDoc();
> > statement.  The CRuntimeClass provides the needed data about the object.
> > The same ability is used by CArchive to create objects from the file stream. Instead of having a 'new' statement for each possible
> > class it uses CreateObject to create all objects. Scott McPhillips [VC++ MVP]
>
> Ok, that verbalizes more clearly, but if I may be so curious why does
> my CMapStringToString Object which is already in existence need
> CreateObject to create a object to receive my serialized read ?
> It appears the WriteClass and ReadClass (called by WriteObject or
> ReadObject ) has no problem fetching the CmStS's Cruntime data
> from my file. So after that, I don't see why another object needs created
> before actually reading the CMap in ?

Because that's how you wrote your code (and it's wrong). The problem
is this snippet:

pExpMap1 = &ExpMap1; // 0
if (ar.IsStoring())
{
ar << pExpMap1;
}
else
{
ar >> pExpMap1;
}

0 is rather unusual and normally a mistake. In serialization, when the
object you want to serialize is "embedded" into another object (as is
the case with your ExpMap1 - it's a member of your document), you
___don't___ use operators >> and <<. (Didn't I explained this you you
earlier? Maybe explanation wasn't good enough?).

Instead, you simply do:

ExpMap1.Serialize(ar);

For ExpMap1 defined as it is, that's it. No "if (storing)", no
additional pointer, no nothing! Just call Serialize, end of.

If the object in question is something you made, and has a versionable
schema (which is the usual case), you do:

ar.SerializeClass(m_object.GetRuntimeClass()); // ^^^^
m_object.Serialize(ar);

If you hold the object in question as a pointer (on the heap), only
then you do:

if (ar.IsStoring())
ar << m_pObject;
else
ar >> m_pObject;

So... What you tried is completely wrong - you just mixed everything
in a bad way. It could be that you don't understand how objects on
heap work, it could be that you don't understand serialization, or it
could be something else, I don't know.

About why there's a call to CreateObject while loading: well, it's
there because that's how operator>>(CArchive, CObject*&) work, and you
put that call in.

Finally, files that you already saved in there are bad and when you
fix your code, you won't be able to load them. If backward-
compatibility is important (I doubt you are in that stage), then I
would do the following:

class CFileHandlingDoc
{
CFileHandlingDoc() : m_pExpMap(new CMapStringToString) {}
void DeleteContents() { delete m_pExpMap; m_pExpMap = NULL; }
CMapStringToString m_pExpMap;
// No "embedded" object anymore;
}

CFileHandlingDoc()::Serialize(...)
{
if (storing)
ar << m_pExpMap;
else
ar >> m_pExpMap;
}

IOW, since you saved your map as pointer on the heap, make it a
pointer on the heap and continue like so. That might be suboptimal
(one more heap pointer, a couple of calls to new/delete), but I will
put my hand in fire that you can't ever notice a difference.

Goran.

^^^^ (I told you already that MSDN explanation on what SerializeClass
is for, whilst correct, is also useful when serializing "embedded"
objects; if ever internal workings of MFC change so that it does
something differently, then I'll start to be wrong; but since
SerializeClass is like it is for, I dunno, 15 years, I doubt it can
ever change).
From: Giovanni Dicanio on
On 06/06/2010 19:19, Joseph M. Newcomer wrote:

> Normally, the destructor invoked when your CDocument is destroyed should delete this.
> However, I've just read the code for CMapStringToString::~CMapStringToString and I don't
> see where this actually frees the storage.

In VC10, the destructor of CMapStringToString calls RemoveAll() method,
so this should be just fine:

<code>

CMapStringToString::~CMapStringToString()
{
RemoveAll();
ASSERT(m_nCount == 0);
}

</code>

Giovanni

From: RB on

> Because that's how you wrote your code (and it's wrong).

Hi Goran, yes you are correct, and I "finally" figured that out on my
own (see my later posts further down the thread ) I wish you had been
able to reply previously, but actually Joe was pointing me to the same
conclusion, he just did not have the time to elaborate as you did. But
then struggling with my own mistakes are driving the results deeper so
maybe I am learning more solid.

> In serialization, when the object you want to serialize is "embedded"
> into another object (as is the case with your ExpMap1 - it's a member
> of your document), you ___don't___ use operators >> and <<.
> (Didn't I explained this you you earlier?

Yes you did, and I believe I fully conceptualize it now, the
abbreviated docs example with my comments.

CMyObject m_myob; // Embedded object
m_pOther = new COtherObject; // type known and
// object already allocated
m_pObDyn = NULL; // To be allocated in another member
// function if needed, could be a derived class object.
-----------------------------
// NOT optional, required method.
m_myob.Serialize( ar ); // Serialize embedded member.
m_pOther->Serialize( ar ); // Serialize objects known
// already allocated
if ( ar.IsStoring() )
{
ar << m_pObDyn; // Serialize unknown to be
} // allocated
else
{
ar >> m_pObDyn; //Polymorphic reconstruction
}
--------------------------
> Maybe explanation wasn't good enough?).

No the explanation was good, but I just did not quite grasp the
magnitude. I mistook the embedded call as an option rather than a
requirement and not just from your explanation but reading my docs
also. But as I stepped thru the code I could see something was not right
and therefore began to dig deeper and also bang my foo bar head to
wake up to the scenario.
My Dad used to say "some learn slower than others" and in this case
that would be me. He also used to say "the sad thing is some never learn"
and at least in this case I have avoided that scenario.
But the peripheral effects of all my experiments and stepping thru
framework code has taught me more than I ever would have probably
learned of MFC otherwise. I really enjoy programming and I like the
challenge. Maybe at some point I can reach plateau so I can actually
begin to see the paradigm in a different light rather than just a light at the
end of a tunnel.
Thanks ever so much for "again" for your patience in showing me the way.
RB


From: Joseph M. Newcomer on
See below...
On Sun, 6 Jun 2010 15:14:03 -0400, "RB" <NoMail(a)NoSpam> wrote:

>I' m in and out today so I will try to respond to some of this
>now before I head back out.
>
>>> CMapStringToString ExpMap1; // is serializable
>>> CMapStringToString* pExpMap1;
>> ****
>> Whenever I see something like this, I have to ask "why do you
>> need the pointer variable?" In fact, I cannot imagine a single reason
>> you would need a pointer variable that pointed to the map, if the sole
>> purpose of this pointer variable is to hold a pointer to the map
>> directly above it.
>> ****
>
>Well you are correct, there is no real reason and you have told me
>so before so I have no excuse. I should have coded this as you
>showed further down.
>
>> ar << &ExpMap1;
>
>> ****
>> Is this the right way to serialize? I would think that this would serialize
>> the POINTER to the map, not the map itself, which would not be useful.
>> If you needed to serialize the pointer, you would have written
>> ar << &ExpMap1;
>> which means the variable, as I suspected, is completely useless. But I
>> also suspect this is the wrong usage. I just went and read the code to
>> CStringMapToString::Serialize and it would not be invoked if you asked
>> it to serialize the *pointer* to the structure.
>> ****
>
>Well the way I understood it was that if the class was serializable, which
>CMapStringToString is, that there is an operator in the CArchive code
>to handle the object pointed to with it's cruntime data. It does in fact write
>the same data to file as the alternative method (and reads it back in) but the
>memory is leaking.
>
>> ****
>> Normally, the destructor invoked when your CDocument is destroyed
>> should delete this. However, I've just read the code for
>> CMapStringToString::~CMapStringToString and I don't see where this
>> actually frees the storage. I'd have to do some experimentation to figure
>> this out, and I don't have time to do this right now. However, I'd suggest
>> making sure that your CDocument-derived object is, in fact, deleted before
>> the program exits. joe
>> ****
>
>Ok, when I get in tonight I will see if I can step thru and find that. I would
>certainly hope so though since it was all project created when I first created
>the project with the whole Document template macros.
>
>>>// I think maybe as Goran had told me some time back that
>>>// using the SerializeClass function is the safer method to use.
>> ****
>> It may be. I wouldn't touch the MFC serialization for any amount of money;
>> I consider it fragile and of very weak design. I prefer to do my own
>> serialization as XML data, but then, I always have control of every data
>> structure used. joe
>> ****
>
>Well that is the very reason I want to understand what is going on with
>the MFC serialize because if I use it to store my app's file data and it someday
>breaks I need to know what has been written so I can code to read it back
>in. I have learned by experimentation and reading the Archive docs that I can
>readily identify every byte in a file dump and know what it is whether it be the
>FFFF new class tag or the schema or the filename string length or that actual
>CmStS value length counts, and the actual keys and values.
>Of course that statement is somewhat self defeative, since I guess I should
>about now be thinking of just writing my own serialize code from the start so
>I know going in what is going on. But the serialize of MFC looked so
>convenient I kinda wanted to utilize it but also wanted to feel confident
>in using it which is why I stepped thru so much of it's code.
>Later.......
***
The biggest problem with MFC serialization is that it generally requires that the data
structure remain constant. Thus, if you change your structure in any way, there is a good
chance that you will get a serious (access-fault-class) error in trying to read in the
older file. With a lot of work and care this can be avoided, but by the time you go
through that much effort, doing something much more robust-by-design is usually easier.

It looks convenient, it *is* convenient, but the seductive simplicity hides a lurking time
bomb. This is why a lot of people use either XML files or other text file formats, and
even then you have to spend a little time dealing with the "backward compatibility"
problem. But when you control the format, it is not as difficult.

Note that for some objects, e.g., embedded spreadsheets, etc., you *have* to use the
built-in object serialization. But then, most of the people who have built these (e.g.,
Office) have already written the complex code to deal with compatibility issues. I just
found that once you have to write all the necessary compatibility code, serialization
doesn't buy anything.
joe

>
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm