I have two serialize operator overloads:
friend CArchive& operator<<(CArchive& rArchive, S_MEMORIAL_INFO const& rsMI)
{
return rArchive << rsMI.strHost
<< rsMI.strCohost
<< rsMI.strZoomAttendant
<< rsMI.strChairman
<< rsMI.strPrayerOpen
<< rsMI.strPrayerClose
<< rsMI.strSpeaker
<< rsMI.strImagePath
<< rsMI.strTextBeforeImage
<< rsMI.strTextAfterImage
<< rsMI.iImageWidthAsPercent
<< rsMI.iSongOpen
<< rsMI.iSongClose;
}
friend CArchive& operator>>(CArchive& rArchive, S_MEMORIAL_INFO& rsMI)
{
return rArchive >> rsMI.strHost
>> rsMI.strCohost
>> rsMI.strZoomAttendant
>> rsMI.strChairman
>> rsMI.strPrayerOpen
>> rsMI.strPrayerClose
>> rsMI.strSpeaker
>> rsMI.strImagePath
>> rsMI.strTextBeforeImage
>> rsMI.strTextAfterImage
>> rsMI.iImageWidthAsPercent
>> rsMI.iSongOpen
>> rsMI.iSongClose;
}
But I now wat to introduce version tracking so that I can cope with new fields without causing crashes in my software for users.
So now I would like:
friend CArchive& operator>>(CArchive& rArchive, S_MEMORIAL_INFO const& rsMI)
{
WORD wVersion{};
rArchive >> wVersion
>> rsMI.strHost
>> rsMI.strCohost
>> rsMI.strZoomAttendant
>> rsMI.strChairman
>> rsMI.strPrayerOpen
>> rsMI.strPrayerClose
>> rsMI.strSpeaker;
rsMI.strTheme.Empty();
if(wVersion >= 2)
rArchive >> rsMI.strTheme;
return rArchive >> rsMI.strImagePath
>> rsMI.strTextBeforeImage
>> rsMI.strTextAfterImage
>> rsMI.iImageWidthAsPercent
>> rsMI.iSongOpen
>> rsMI.iSongClose;
}
friend CArchive& operator<<(CArchive& rArchive, S_MEMORIAL_INFO& rsMI)
{
WORD wVersion = 2;
return rArchive << wVersion
<< rsMI.strHost
<< rsMI.strCohost
<< rsMI.strZoomAttendant
<< rsMI.strChairman
<< rsMI.strPrayerOpen
<< rsMI.strPrayerClose
<< rsMI.strSpeaker
<< rsMI.strTheme
<< rsMI.strImagePath
<< rsMI.strTextBeforeImage
<< rsMI.strTextAfterImage
<< rsMI.iImageWidthAsPercent
<< rsMI.iSongOpen
<< rsMI.iSongClose;
}
How can I now cope with the fact that for some users there is no WORD
value there in the archive?
With hindsight I would have designed the serialization to write a version number right from the outset, but too late for that now.
As noted in the comments, the problem is that when reading wVersion
from the archive this removes two bytes from the CString
object following. CArchive
performs its own buffer management. The class definition contains several member variables for this, and it's quite easy to find out what they are doing. These members are protected though, so we need to define a derived class, containing a member which sets the current buffer pointer two bytes back:
class CArchiveHlp : public CArchive
{
public :
void GoBack2()
{
if (m_lpBufCur - m_lpBufStart < 2)
AfxThrowFileException(CFileException::genericException);
m_lpBufCur -= 2;
}
};
Then you can use this class in your code like this:
rArchive >> wVersion;
if (wVersion != 2) // Or any other valid version number
{
((CArchiveHlp*)&rArchive)->GoBack2();
wVersion = 0;
}
rArchive >> rsMI.strHost
.
.
The (CArchiveHlp*)
cast is wrong in some way because the actual object is not CArchiveHlp
(a dynamic_cast
would fail here), however the classes are almost identical, and the GoBack2()
member is not virtual (so no v-table), therefore you can call it without any problem - it calls CArchiveHlp
code for a CArchive
class instance, whose data in memory are identical.
It's tested and works.