From: Joseph M. Newcomer on
See below....
On Sun, 20 Jun 2010 20:01:27 -0400, "RB" <NoMail(a)NoSpam> wrote:

>
>Hey Joe thanks for replying
>
>> It does seem odd, but it also means that they are not assuming you require serialzation.
>> I'd be in the other fix, having to constantly get rid of DECLARE/IMPLEMENT_SERIAL
>> because I don't ever want to use the MFC serialization mechanism.
>> ****
>
>Well actually that is a very conceivable reason for not creating serial, so that makes two
>reasons so far. Since as Goran had pointed out to me serial ties the doc derivative class's
>name to the serialize schema. So I surmise that in future apps that had a slightly different
>name and doc derivative class name would break the Runtime class data match for a read
>from a newer version. That makes sense as to why the help texts always seem to point to
>making a separate CObject derivative for SERIAL creations.
> The thing that got me is why they did not be more clear about it and say it ?
>
>> In a sane worled, there would be SerializeIn(CArchive & ar) and SerializeOut(CArchive &
>> ar). And the stupid IsStoring method would not have to exist.
>> I think these are the same people who think that creating more files is evil.
>> ****
>
>Well as far as the mfc serialize scheme goes I can see that putting the storing and
>loading loops together helps keep the same order of writes and reads in my face
>so it might be visually less error prone. But I guess you could do that in your
>method also so that doesn't hold much water.
> But actually now I feel like I will use the dyncreate method and devise my own
>schema method for doc derivative members that need stored and keep the mfc
>serialize runtime class method for CmStS or any CObject derivatives I may need
>to create Serial. At least for the time being. As I get more experience under my
>belt I may forgo all of this ( like you) and just write my own persistence functions.
****
The fact that you would NEED to keep them in sync is one of the serious failures of the
whole mechanism! In a well-designed system (the kind we did back in 1979, and the design
was mine) the ORDER of the values being saved was completely irrelevant; so I could save
fields A, B and C and reading them back would fill in fields A, B and C without my
worrying about the fact that yesterday the struct had the layout {int A; int B; int C;}
and today it has {int C; int B; int A; }. The data was self-indentifying. We could even
deal with the fact that field D had been added, and field A eliminated. In fact, it was a
*lot* more flexible than that, and it all was built-in to the serialization mechanism, so
the programmer NOT ONCE had to think about the "order" of saving the information. Fully
automated, completely transparent, error-resistant, handled schema evolution gracefully.
Let's see: MFC serialization is not transparent, is not error-resistant, requires MASSIVE
effort to handle schema evolution...

Gee, we solved this in 1979. I even co-authored a book about it in 1990. And STILL the
same failures are repeated, yet again. And it is now 30 years since we showed that you
can do the job RIGHT without involving massive effort on the part of the programmer.

And thanks to Steve Hobbs, who was then the maintainer of the debugger, the debugger was
fully integrated into the whole mechanism, so you could display the structures easily. It
took C until the mid-1980s to accomplish that! As far as I know, we had one of the first
structure-aware debuggers.
joe
****
>
>>>const CRuntimeClass CFileHandlingDoc::classCFileHandlingDoc =
>>> { "CFileHandlingDoc", sizeof(class CFileHandlingDoc),
>>> 0x0000, // !RB replaced the 0xFFFF,! ****************
>> ****
>> Why? Just curious.
>> ****
>
>Nothing except having read the Laura said the framework supported serialize in the
>doc class without creating with Serial, I wanted to see if I could insert my schema of
>zero into the expanded dyncreate macro and then write and read it back. And I found
>that it did not work, so she must have been referring to some other aspect of
>framework support. It did write ok, I could see it in the dump. But it failed to read it
>back I presume since it did not have all of the serial macro capability to compare the
>written Class runtime data.
****
There's already a built-in versioning system for it. It was added as an afterthought,
when it became obvious that, guess what, schemas *evolved*. In 1979, my design *assumed*
that schemas evolve, and the reader and writer had to work correctly *in that context*.
(This was actually based on the ideas I developed so I could build the program that
demonstrated the ideas of my PhD dissertation; I didn't have time to deal with all the
conventional complex and error-prone code and still graduate before my funding ran out!)
In MFC, it was a surprise. Whoopsie.

In a sane world, there would be no "isstoring" test done by the programmer; instead, you
would have macros
TRANSPUT(field)
which would decide whether the field was being read or written. In fact, the only time I
ever used MFC serialization, I had

#define TRANSPUT(ar, field) if(ar.IsStoring) ar << field else ar >> field
so there was never a situation in which I could write a file that could not be read by
the SAME code. But it still broke under schema evolution. I told the client that this
would happen, and nobody believed me, and they had insisted we use MFC serialization. It
was a serious mistake. And it convinced me that I would never, ever, use MFC
serialization again.
joe
****
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Goran on
On Jun 21, 12:01 am, Joseph M. Newcomer <newco...(a)flounder.com> wrote:
> In a sane worled, there would be SerializeIn(CArchive & ar) and SerializeOut(CArchive &
> ar).  And the stupid IsStoring method would not have to exist.

+1 for that. MFC is the only serialization scheme that dumps load and
store code in one function with an "if storing store; else load".

That ends in a myriad of "ifs" all over the place, whereas there
should be none (and who didn't put operator >> in load branch and vice-
versa?). Loading and storing are two separate branches of code. Sure,
they should be kept close for obvious reasons, but an "if" puh-lease!
He who decided that was an idiot.

( To the guy who did decide that: no, it does not matter that decision
was made long time ago when adding a virtual function call entry might
have been considered expensive. ;-) )

Goran.
From: Goran on
Hi.

MFC serialization and other serialization schemes use class name to
identify the class in the saved stream. This is quite reasonable - you
want to store and load an object whose class is known at runtime only.
How do you do that? Save/load some sort of class marker. What should
that marker be? Class name is simplest possible and quite obvious
answer.

Obvious downside is that if you subsequently change class name,
loading of old files will fail. So clearly, you need to keep that
name, and serialization code, for as long as you want to read said old
files.

The document name is a sticky point, because it's at the very
beginning of the file. If you go with Doc.SerializeClass, and you do
end up wanting to change doc name, find me on the internet ( this
newsgroup will be dead by then ;-) ), we can work it out +/- easily.

Note that this goes for every object saved with SerializeClass
(including using operators >> / <<). Document is, I'd say, the least
of your problems, really, because, why would you want to change it?
It's set once and for all, and the more code goes on, the more you
depend on it. I'd say, you won't change it for an important reason. In
fact, there is a pretty good chance that you will not change it at
all, not if you give it a meaningful name.

Other thing: don't look at DECLARE_SERIAL in MFC, it's not using
versionable serialization, for reasons I explained in earlier posts.
Client code, however, does need versioning. Use VERSIONABLE_SCHEMA|
YOUR_SCHEMA_NUMBER_HERE. If you don't, MFC will refuse to load a class
if there's a schema mismatch - you don't want that, don't you? ;-) If
you __do__ use VERSIONABLE_SCHEMA, you can use ar.GetObjectSchema()
and load schema-specific stuff as desired.

Goran.
From: RB on

> Client code, however, does need versioning. Use VERSIONABLE_SCHEMA|
> YOUR_SCHEMA_NUMBER_HERE. If you don't, MFC will refuse to load a
> class if there's a schema mismatch - you don't want that, don't you? ;-) If you
> __do__ use VERSIONABLE_SCHEMA, you can use ar.GetObjectSchema()
> and load schema-specific stuff as desired.
> Goran.

Oh ok, I think I see what you are saying now. So if I,

IMPLEMENT_SERIAL (CObject_Derivative, CObject, 1)

And then in a later revised class I,

IMPLEMENT_SERIAL (CObject_Derivative, CObject ,
2 | VERSIONABLE_SCHEMA)

When the updated program reads a CObject_Derivative object whose
schema number is 1, MFC won't throw a CArchive exception because of
the VERSIONABLE_SCHEMA flag in the schema number. But it will know
that the two schemas are different because the base schema number was
increased from 1 to 2.
Yea I can see that. I might be now eligible to graduate from level Dummy 0
to Dummy 1.0 ;-) Thanks again for all your help. My next study is to learn
how to implement exception handling into my code.
Later.........RB



From: RB on

Well, even though light years above my level, I can see that if you
had been on the mfc design team, many of my questions on
Serialization would have never had to be asked. Since your design
would have eliminating many ambiguities and facets I have struggled
to understand. Course most other dummys of my same competence
level would not have the morbid curiosity that I do and would
probably have just >> and << never have asked them anyhow.
But indirectly it has helped me learn more of the mfc structure.
Thanks again. RB