0

I need to serialize a vector of pointers to base class and derived class. Serialize function overloaded for both classes, so I did it succesfully like this:`

CFile out;
if (!out.Open(filename.c_str(), CFile::modeWrite | CFile::modeCreate))
    return false;

CArchive ar(&out, CArchive::store);
for (auto it = container_.begin(); it != container_.end(); ++it)
{
    (*it)->Serialize(ar);
}
ar.Close();
out.Close();

So the question is, how should I DEserialize it now? I have no ideas about calling correct constructor while reading objects from CArchive...

Uroboros
  • 189
  • 2
  • 12
  • 1
    You don't call the constructor at all. You simply use `operator>>` of the `CArchive` class, and get a pointer to a fully constructed object back (see [Storing and Loading CObjects via an Archive](https://msdn.microsoft.com/en-us/library/3bfsbt0t.aspx)). The way you are storing the vector, however, makes it unrecoverable: You are missing the count of elements, so when reading the archive back in, you don't know when to stop reading additional elements. You'll have to write the count first. Using the MFC container classes do all of that automatically. – IInspectable Nov 14 '15 at 12:56

1 Answers1

1

You'll first need to save out the count of elements in the container (using ar.WriteCount). Then (since your container has multiple types in it) for each element you serialize you'll need to include extra data to tell you what the type of that element is. This could be just one extra character (0 = base class, 1 = first derived class), another count type number (written with WriteCount), or something more elaborate like type names.

To read it back in you read the element count (using ar.ReadCount), then for each element to read in you read the type character, count, or whatever, allocate a new element of that type, deserialize into the newly allocated element, and finally store the allocated element into the container you are deserializing.

I needed to do something similar many years ago in order to transition from MFC containers to STL ones, and the implementation of Serialize used by the MFC containers (in <afxtempl.h>) was very helpful.

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
  • Thanks a lot, but as it written in MFC docs, serialize stores runtime type information along data(CRuntimeClass or something like this). Can we use it instead of writing extra type information? – Uroboros Nov 14 '15 at 06:44
  • 1
    There is no `CArchive::WriteCount` or `CArchive::ReadCount`. Storing type information is not needed either (the MFC serialization infrastructure does that already for you). You simply have to call `ar >> pObj` and the framework will return a pointer to the fully constructed object. The object will be of the correct type, even if you pass a pointer to a base class (usually `CObject`) to `operator>>`. – IInspectable Nov 14 '15 at 13:03
  • @IInspectable They're undocumented (not something you can tell from looking at the class definition in afx.h), but are public members of `CArchive` used by `CList::Serialize` and other related functions. It's been quite a while since I've used CArchive and forgot about the class details. – 1201ProgramAlarm Nov 15 '15 at 00:17
  • @Uroboros Yes, just store the object like is mentioned in IInspectable's comment. – 1201ProgramAlarm Nov 15 '15 at 00:18
  • @IInspectable Great, i fixed it! But it works well only if I pass a pointer to `CObject`, but not to `MyBaseClass` which is derived from it (I got `CArchive exception: badClass` in this case). Also i didn't mention I'm serializing a vector of shared_ptrs, and thus I need to use casts from `CObject` to `MyBaseClass` to construct shared_ptr... Why could I get an exception? – Uroboros Nov 15 '15 at 08:37
  • @Uroboros: You get a `badClass` exception when trying to (de-)serialize an unknown class. Is your `MyBaseClass` both derived from `CObject`, and declared as being serializable ([DECLARE_SERIAL](https://msdn.microsoft.com/en-us/library/5593z07w.aspx) and [IMPLEMENT_SERIAL](https://msdn.microsoft.com/en-us/library/a94da1y0.aspx))? The latter registers the class in a global registry used for deserializing. This registry serves as a lookup table for factory methods based on string representations of class names. – IInspectable Nov 15 '15 at 13:27
  • @IInspectable: Yep,`MyBaseClass` derived from `CObject` and `MyDerivedClass` derived from `MyBaseClass` both declared serializable as you said. – Uroboros Nov 15 '15 at 17:54
  • @Uroboros: In that case I would assume, that the issue is with your code, that serializes the objects to the `CArchive`. Specifically, the call to `Serialize` should be replaced with `operator>>`. Otherwise, no type information is stored into the serialized stream. [Storing and Loading CObjects via an Archive](https://msdn.microsoft.com/en-us/library/3bfsbt0t.aspx) notes that *"whether you use the CArchive << and >> operators, versus calling Serialize, depends on whether you need the loading archive to dynamically reconstruct the object based on previously stored CRuntimeClass information."* – IInspectable Nov 15 '15 at 18:04
  • @Uroboros: Do your implementations of `Serialize` call the base class implementation prior to (de-)serializing the derived class? You can also use the debugger to break into the code, when a `badClass` exception is raised. To do so, go to *Debug* -> *Windows* -> *Exception Settings* and enable `CException` from the *C++ Exceptions* section. That way you can inspect the source code that raises the exception. – IInspectable Nov 18 '15 at 22:14
  • @IInspectable Finally I found the problem. It was I declared my classes serializable, but in IMPLEMENT_SERIAL of MyDerivedClass I declared it as derived from CObject, not from MyBaseClass(as actually). Thank you a lot for ur patience. – Uroboros Nov 21 '15 at 11:04