CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 14 of 14
  1. #1
    Join Date
    Aug 2005
    Posts
    167

    Question Load, Saving and backwards compatible.

    Hi there,

    This is really a concept question. I think most of us know how to enable backwards compatiblity. In other words a file saved in version 1, of our app, can be opened in version two. This is relatively simple because version 2 is aware of version 1's data structure and so it can open it accordingly.

    However what do u think would be the best way to open a version 2 document in version 1? I know that MS word and lots of other apps can do this, so its clearly do-able? My app has lots of different objects, all of which have their own serialise methods. I use a CArchive from a file, and maybe my understanding of the carchive is weak, but how do you know you've reached the end of the archive that pertains to the problem.

    Take the issue -

    Version 1 - The CodeGuru object, saves these attributes.

    CString
    bool,
    int
    int
    int

    Version 2 - The CodeGuru object, saves these attributes.

    CString
    bool,
    int
    int
    int
    bool - saves extra bool for some new feature lets say.

    Well if you read in the version 2 document in version 1, i assume you would get this.

    CString - read in fine
    bool- read in fine
    int- read in fine
    int - read in fine
    int - read in fine

    this would be the end of the first code guru object so you start on the next.
    CString - read in wrong because the next type would in fact be a bool.

    Do you see what i mean? i hope i have explained this well. Does the carchive have an end - of - this object flag? or do we as developers have to implement this

    Thanks really interested to see what people have to say on this

    Will

  2. #2
    Join Date
    May 2006
    Posts
    327

    Re: Load, Saving and backwards compatible.

    You can consider the following idea.

    In you CodeGuru object, before serializing members, write the size of the object:

    Code:
    ar << sizeof(CodeGuru);
    In de-serializing operation, first load this size:

    Code:
    size_t size;
    ar >> size;
    Then analyze the difference between read value and the real size, sizeof(CodeGuru). If the read value is greater, then you have larger object written with newer version of your application.

    Read the rest of your members. If you detected then the data are from newer version, then you should skip a portion, which is equal to

    Code:
    size_t const delta = size - sizeof(CodeGuru);
    In order to skip unrecognizable data, you can execute:

    Code:
    ar.Flush();
    ar.GetFile()->Seek(delta, CFile::current);
    Perhaps this approach can inspire you. Unfortunately, it seems to be inapplicable in case you serialize pointers.

  3. #3
    GCDEF is offline Elite Member Power Poster
    Join Date
    Nov 2003
    Location
    Florida
    Posts
    12,635

    Re: Load, Saving and backwards compatible.

    Your premise is mistaken. Very few apps, including MS Word are forward compatible. I suppose you could put some version ID in your code and bail out of your serialize function when you get to a certain point, but forward compatibility isn't normal nor expected generally.

  4. #4
    Join Date
    Aug 2005
    Posts
    167

    Re: Load, Saving and backwards compatible.

    Wow, thanks Viorel

    thats an excellent idea I was unaware we could do that. So before I save any information on an object i must save that objects saved size, if i am reading that correctly?

    thank you for that its very usefull

    and to GCDEF.

    You say i am mistaken about the forwards compatiblity? I am able to open an MS Word document saved in MS word 2003, in MS Word 2000. Is this not forwards compatible? and if not, how are they doing it?

    Thanks

    Will

  5. #5
    Join Date
    May 2006
    Posts
    327

    Re: Load, Saving and backwards compatible.

    Quote Originally Posted by wdhough
    So before I save any information on an object i must save that objects saved size, if i am reading that correctly?
    Yes. You can also use "ar << sizeof(*this)" (instead of "ar << sizeof(MyClass)"), which is easier to duplicate in your project with cut and paste.

  6. #6
    Join Date
    Feb 2002
    Posts
    4,640

    Re: Load, Saving and backwards compatible.

    Quote Originally Posted by wdhough
    and to GCDEF.

    You say i am mistaken about the forwards compatiblity? I am able to open an MS Word document saved in MS word 2003, in MS Word 2000. Is this not forwards compatible? and if not, how are they doing it?

    Thanks

    Will
    Technically, it's probably not. The format of a DOC file probably didn't change, hence you can open Word 2003 docs in Word 2000.

    Try opening a Word 2003 doc in a version of Word pre 95. Bet it doesn't work at all.

    Viggy

  7. #7
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,234

    Re: Load, Saving and backwards compatible.

    The MFC way:

    Derive from CObject and use DECLARE_SERIAL/IMPLEMENT_SERIAL macros and CArchive::GetObjectSchema.

    For more explanations and example see CArchive::GetObjectSchema
    as well as
    Serialization: Making a Serializable Class.
    Last edited by ovidiucucu; June 7th, 2006 at 10:06 AM.
    Ovidiu
    "When in Rome, do as Romans do."
    My latest articles: https://codexpertro.wordpress.com/

  8. #8
    Join Date
    Jan 2002
    Location
    Houston, TX
    Posts
    1,421

    Re: Load, Saving and backwards compatible.

    Quote Originally Posted by wdhough
    You say i am mistaken about the forwards compatiblity? I am able to open an MS Word document saved in MS word 2003, in MS Word 2000. Is this not forwards compatible? and if not, how are they doing it?

    Thanks

    Will
    You may find that Word 2003 uses the exact same file format as Word 2000 and that's why it seems to work. Perhaps the changes were in other areas - features that were added and so on.

    More commonly, you'll find that by serializing the version number that saved the file (say it was saved by version 2) an application will then read the version number first, and if it's a "future" version (trying to open in version 1) simply display a message that says

    "You need an updated version of the software to open this file."

    I've seen that frequently.

    Good luck.
    Be sure to rate those who help!
    -------------------------------------------------------------
    Karl - WK5M
    PP-ASEL-IA (N43CS)
    PGP Key: 0xDB02E193
    PGP Key Fingerprint: 8F06 5A2E 2735 892B 821C 871A 0411 94EA DB02 E193

  9. #9
    Join Date
    Aug 2005
    Posts
    167

    Question Re: Load, Saving and backwards compatible.

    Viorel,

    Thanks for your help so far, there is something you said that still confuses me.

    You said

    "Unfortunately, it seems to be inapplicable in case you serialize pointers."

    Does this mean that your idea would not work if i were serializing pointers? or that it would only work if you were serializing pointers. and if so why?

    thanks

    Will

  10. #10
    Join Date
    Feb 2003
    Location
    Iasi - Romania
    Posts
    8,234

    Re: Load, Saving and backwards compatible.

    Quote Originally Posted by Viorel
    Unfortunately, it seems to be inapplicable in case you serialize pointers.
    Well, it seems, because as long as pointers represents memory addresses, what use to "serialize pointers"?

    @to wdhough: Have you read at least just a little my first answer?
    Ovidiu
    "When in Rome, do as Romans do."
    My latest articles: https://codexpertro.wordpress.com/

  11. #11
    Join Date
    May 2006
    Posts
    327

    Re: Load, Saving and backwards compatible.

    I think the investigated method works only in case of simple serialized data, like int or double. In case of pointers, like CMyObject *, MFC offers a specific method of serialization, which relays on some internal data. Actually, for each pointer to an object derived from CObject * (and declared with DECLARE_SERIAL macro), the CArchive class writes the name of the class, and other values. This information is used later when you de-serialize objects.

    Therefore the size of written information will be greater then the size of serialized class, i.e. sizeof(MyClass). So it will be difficult to known which part of input stream belongs to newer version and therefore should be skipped during de-serialization.

    The string objects like CString affects the size of written data too.

    You can investigate another approach, which is based on object-size information too. Before you start serialization, obtain the position of the file pointer:

    Code:
    ar.Flush();
    ULONGLONG pos1 = ar.GetFile()->Seek(0, CFile::current);
    Then write a dummy value:

    Code:
    ar << (ULONGLONG)0;
    Next, serialize the version number of you class, for instance:

    Code:
    ar << 3;
    Next, serialize your class content, for instance:

    Code:
    ar << mVal1 << mVal2 // version 1
        << mVal3 // added in version 2
        << mVal4 << mVal5; // added in version 3
    After this, obtains the new position of the file pointer:

    Code:
    ar.Flush();
    ULONGLONG pos2 = ar.GetFile()->Seek(0, CFile::current);
    Calculate the size of written part:

    Code:
    ULONGLONG size = pos2 - pos1;
    Then return back and write this value to the dummy placeholder:

    Code:
    ar.GetFile()->Seek(pos1, CFile::begin);
    ar << size;
    ar.GetFile()->Seek(pos2, CFile::begin);
    Now your file contains the size of written data and the version number.

    When you de-serialize it, load the size and the version:

    Code:
    ULONGLONG size;
    int version;
    
    ar >> size >> version;
    Also get the file position:

    Code:
    ar.Flush();
    ULONGLONG pos1 = ar.GetFile()->Seek(0, CFile::current);
    Then check the version. De-serialize only data which belong to that version, for instance:

    Code:
    if( version <= 1)
    {
        ar >> mVal1 >> mVal2;
    }
    
    if( version <= 2)
    {
        ar >> mVal3; // in version 2, your added mVal3
    }
    
    if( version <= 3)
    {
        ar >> mVal4 >> mVal5; // in version 3, your added mVal4 and mVal5
    }
    else
    {
    ... else this is a newer version of the file. You should skip unrecognizable data. This can be done like this:

    Code:
    ar.Flush();
    ar.GetFile()->Seek(pos1 + size, CFile::begin);
    I think this idea can be experimented.

    For the other problem, when you need only to load earlier files in newer application, MFC offers a feature to store the version number for you. See the "schema" parameter of IMPLEMENT_SERIAL macro. In this case you can obtain the version using CArchive::GetObjectSchema function, and then analyse it. But the engine cannot be used for inverse case.

    That is my understanding.

  12. #12
    Join Date
    Aug 2005
    Location
    Netherlands, The
    Posts
    2,184

    Re: Load, Saving and backwards compatible.

    viorel serious bug ..

    u use version <= 3. but if the version is 2. this statement will be true and loads data that is not there.

  13. #13
    Join Date
    Aug 2005
    Posts
    167

    Question Re: Load, Saving and backwards compatible.

    Hi,

    what would happen if at the end of my saving for each object i put a flag to say so....or a CString that said end.

    therefore in version one, at the end of the loading stuff, i strip out data until i find this flag and then move on....would that work?

    Will

  14. #14
    Join Date
    Aug 2005
    Location
    Netherlands, The
    Posts
    2,184

    Re: Load, Saving and backwards compatible.

    no.
    why not just read binary myheader with sizeof(myheader)?
    its not diffcult.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured