Serializing abstract objects
Richard Brice -- bricer@wsdot.wa.gov Thursday, December 05, 1996 Environment: VC++4.0 MFC 4.0 WindowsNT 4.0 My document has a container of pointers to objects of type A. Type A is derived from CObject and it is an abstract class (has pure virtual fns). When I save the document in Serialize, I iterate through the container and serialize each element. Since Serialize is virtual, Serialize in a Type A subclass is called and everything is great. Now I want to deserialize the container. How can I reconstruct the correct concrete class so that the proper Serialize method gets called? I think the solution looks something like this: void CMyDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // Write element count // For each element in the container // Write the runtime info // Serialize the element } else { // Read element count // For each element // Read the runtime info // Instantiate the correct Type A subclass // Serialize the element // Add the element to the container } } Any suggestions as to how I might implement this?
Grant Shirreffs Great Elk -- Grant.S@greatelk.com Monday, December 09, 1996 [Mini-digest: 7 responses] Do you mean to use MFC serialisation, that is, via the IMPLEMENT_SERIAL macro? If so, I think you're out of luck. You can't declare IMPLEMENT_SERIAL on an abstract class, as IMPLEMENT_SERIAL expands to include a call to CMyClass::CMyClass( ), which can't be directly called for an abstract class. If you want to use IMPLEMENT_SERIAL, you can't have pure virtual methods. >---------- >From: Richard Brice >Sent: Friday, 06 December 1996 8:47 AM >To: mfc-l >Subject: Serializing abstract objects > > >Environment: VC++4.0 MFC 4.0 WindowsNT 4.0 > >My document has a container of pointers to objects of type A. Type A >is >derived from CObject and it is an abstract class (has pure virtual >fns). > >When I save the document in Serialize, I iterate through the container > >and serialize each element. Since Serialize is virtual, Serialize in a > >Type A subclass is called and everything is great. > >Now I want to deserialize the container. How can I reconstruct the >correct concrete class so that the proper Serialize method gets called? > >I think the solution looks something like this: > >void CMyDoc::Serialize(CArchive& ar) >{ > if (ar.IsStoring()) > { > // Write element count > // For each element in the container > // Write the runtime info > // Serialize the element > } > else > { > // Read element count > // For each element > // Read the runtime info > // Instantiate the correct Type A subclass > // Serialize the element > // Add the element to the container > } >} > >Any suggestions as to how I might implement this? > -----From: "Alexander(Sasha) Grinshpun"If you are dealing with pointer to object, it's generally a better idea to call 'operator <<' for storing this object and 'operator >>' for loading it. There are 2 good reasons for this: 1. In this case archiver will store automatically runtime type information for this object (it's exactly what you are looking for) 2. (much more important) Archiver will preserve the morphology of your objects network. This means that if you have a number of objects that points to the same object, this relationship will be kept across storing/loading sessions. In such a case your serialization code will look like: CSomeContainer::Serialize(CArchive &ar) { if(ar.IsStroring()) { ar << number_of_elements; foreach pelement in thiscollection { ar << pelement; } } else if(ar.IsLoading()) { ar >> number_of_elements; for(int i = 0; i < number_of_elements; i++) { CObject *tmp; ar >> tmp; Insert((CElementOfSomeCollection *) tmp); } } If you are sure that each element in your collection is unique (so the reason #2 isn't important to you) you may save a (little) time in serialization process by using Serialize instead of operator>> and operator<< and storing runtime type information by yourself. (In this case you will save pointer lookup in the hash table that archiver maintains for storing pointer to objects). Your code for this will look something like CSomeContainer::Serialize(CArchive &ar) { if(ar.IsStroring()) { ar << number_of_elements; foreach pelement in thiscollection { // fetch runtime type information CRuntimeClass *pclass = pelement->GetRuntimeClass(); // write rtti to the archive ar.WriteClass(pclass); pelement->Serialize(ar); } } else if(ar.IsLoading()) { ar >> number_of_elements; for(int i = 0; i < number_of_elements; i++) { CObject *tmp; CRuntimeClass *pclass = ar.ReadClass(); CElementOfSomeCollection *pelem = (CElementOfSomeCollection*)pclass->CreateObject(); pelem->Serialize(ar); Insert((CElementOfSomeCollection *) tmp); } } Richard Brice wrote: > > Environment: VC++4.0 MFC 4.0 WindowsNT 4.0 > > My document has a container of pointers to objects of type A. Type A is > derived from CObject and it is an abstract class (has pure virtual fns). > > When I save the document in Serialize, I iterate through the container > and serialize each element. Since Serialize is virtual, Serialize in a > Type A subclass is called and everything is great. > > Now I want to deserialize the container. How can I reconstruct the > correct concrete class so that the proper Serialize method gets called? > > I think the solution looks something like this: > > void CMyDoc::Serialize(CArchive& ar) > { > if (ar.IsStoring()) > { > // Write element count > // For each element in the container > // Write the runtime info > // Serialize the element > } > else > { > // Read element count > // For each element > // Read the runtime info > // Instantiate the correct Type A subclass > // Serialize the element > // Add the element to the container > } > } > > Any suggestions as to how I might implement this? -- ---- Thanks, Sasha. -----From: "Michael Potter" Are you trying to re-invent the wheel? If all of your objects are declared from CObject and you have properly used the serialize macros throughout the chain then, let MFC do the work for you. Of course this requires that you are creating and using them dynamically. if (ar.IsStoring()) { // Write element count // For each element in the container // Write the Pointer i.e. ar << MyCObjectTypePtr; } else { // Read element count // For each element // read a CObject * and store in document ar >> MyTmpCObjectTypePtr } Mike ---------- > From: Richard Brice > To: mfc-l > Subject: Serializing abstract objects > Date: Thursday, December 05, 1996 1:47 PM > > > Environment: VC++4.0 MFC 4.0 WindowsNT 4.0 > > My document has a container of pointers to objects of type A. Type A is > derived from CObject and it is an abstract class (has pure virtual fns). > > When I save the document in Serialize, I iterate through the container > and serialize each element. Since Serialize is virtual, Serialize in a > Type A subclass is called and everything is great. > > Now I want to deserialize the container. How can I reconstruct the > correct concrete class so that the proper Serialize method gets called? > > I think the solution looks something like this: > > void CMyDoc::Serialize(CArchive& ar) > { > if (ar.IsStoring()) > { > // Write element count > // For each element in the container > // Write the runtime info > // Serialize the element > } > else > { > // Read element count > // For each element > // Read the runtime info > // Instantiate the correct Type A subclass > // Serialize the element > // Add the element to the container > } > } > > Any suggestions as to how I might implement this? -----From: dhalpin@macromedia.com (David Halpin) I don't have a lot of experience with MFC or know if there is an easy way to solve this with MFC. However, there is an excellent book by James Coplien, entitled "Advanced C++ Programming Styles and Idioms" that talks about creating "Exemplars" (Ch 8, pg 279). This is an idiom that can be used as slick way of evolve an object while reading from a disk file. The object type is not know when the read starts but self modifies as more data is available. Even if you don't use this method for your current needs, I would recommend adding this book to your collection if you don't already have it. David. >Environment: VC++4.0 MFC 4.0 WindowsNT 4.0 > >My document has a container of pointers to objects of type A. Type A is >derived from CObject and it is an abstract class (has pure virtual fns). > >When I save the document in Serialize, I iterate through the container >and serialize each element. Since Serialize is virtual, Serialize in a >Type A subclass is called and everything is great. > >Now I want to deserialize the container. How can I reconstruct the >correct concrete class so that the proper Serialize method gets called? > >I think the solution looks something like this: > >void CMyDoc::Serialize(CArchive& ar) >{ > if (ar.IsStoring()) > { > // Write element count > // For each element in the container > // Write the runtime info > // Serialize the element > } > else > { > // Read element count > // For each element > // Read the runtime info > // Instantiate the correct Type A subclass > // Serialize the element > // Add the element to the container > } >} > >Any suggestions as to how I might implement this? David dhalpin@macromedia.com -----From: mss@tartus.com (Michael Scherotter) Richard Brice wrote: > > Environment: VC++4.0 MFC 4.0 WindowsNT 4.0 > > My document has a container of pointers to objects of type A. Type A is > derived from CObject and it is an abstract class (has pure virtual fns). > > When I save the document in Serialize, I iterate through the container > and serialize each element. Since Serialize is virtual, Serialize in a > Type A subclass is called and everything is great. > > Now I want to deserialize the container. How can I reconstruct the > correct concrete class so that the proper Serialize method gets called? > > I think the solution looks something like this: > Any suggestions as to how I might implement this? If you use a CTypedPtrArray, CTypedPtrList, or CTypedPtrMap of the abstract base class. Then you can use the template class's Serialize() function. class CAbstract, public CObject { ... }; class CConcrete, public CAbstract { ... }; class ACollection: public CObject { CTypedPtrArray m_AbstractList; }; void ACollection::Serialize(CArchive& ar) { m_AbstractList::Serialize(ar); } -- Michael S. Scherotter |Architectural Design Tools Lead Software Developer |AutoCAD Applications Tartus Development, Inc. |Custom CAD Solutions 630 Las Gallinas Ave #300 |__________________________ San Rafael, CA 94903 mss@tartus.com (415) 491-8925 michael@charette.com (415) 491-8921 (fax) 71035.1675@compuserve.com http://www.tartus.com/people/mss ____________________________________________________________ -----From: Paul.B.Folbrecht@JCI.Com You are asking how to implement dynamic object construction, something that MFC serialization already supports- this is in fact one of the best reasons for using it. If your doc's collection class is an MFC collection class, it already support full serialization. You can serialize the whole thing, reading or writing, by call Serialize() on it. Your objects must, of course, have serialization support- use DECLARE/IMPLEMENT_SERIAL and override Serialize(). (For your abstract class, use DECLARE/IMPLEMENT_DYNCREATE instead.) If your collection class is for some reason not an MFC collection, reading in the objects takes just a little more work. You will need to serialize the count, as you've done below, and iterate on reading. Something like this: ... short nCount, nIndex; ar >> nCount; for ( nIndex = 0; nIndex < nCount; nIndex++ ) { CMyObject* pOb; ar >> pOb; coObjects.Add( pOb ); } ... There is no problem here if CMyObject would happen to be abstract. -Paul Folbrecht Compuware Corp. ______________________________ Reply Separator _________________________________ Subject: Serializing abstract objects Author: bricer@wsdot.wa.gov at Mailhub Date: 12/8/96 4:41 PM Environment: VC++4.0 MFC 4.0 WindowsNT 4.0 My document has a container of pointers to objects of type A. Type A is derived from CObject and it is an abstract class (has pure virtual fns). When I save the document in Serialize, I iterate through the container and serialize each element. Since Serialize is virtual, Serialize in a Type A subclass is called and everything is great. Now I want to deserialize the container. How can I reconstruct the correct concrete class so that the proper Serialize method gets called? I think the solution looks something like this: void CMyDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // Write element count // For each element in the container // Write the runtime info // Serialize the element } else { // Read element count // For each element // Read the runtime info // Instantiate the correct Type A subclass // Serialize the element // Add the element to the container } } Any suggestions as to how I might implement this? -----From: "P.J. Tezza" Richard, For each concrete class, use the DECLARE_SERIAL MFC macro in the class = declaration and the IMPLEMENT_SERIAL macro in the class's implementation = file. Store pointers to the concrete objects in your container. Use ar = << pConcreteObject to store concrete objects and ar >> pConcereteObject = to create and load concrete objects. See the MFC Scribble tutorial for = source code you can copy and paste. Depending on your application's = object ownership rules, you may need to destroy the objects when the = container or document is destroyed. You may also need to watch out for = multiple pointers to the same object. I suggest a some serious study = into MFC's serialization support in both the MFC documentation and the = MFC source code before deciding on your application's architecture. PJ pj@exemplarsoftware.com
Roger Onslow/Newcastle/Computer Systems Australia/ Wednesday, December 11, 1996 I did a query for "serialize abstract" in VC++ online documentation and it took me to the KB: C++ Article "Serializing an Abstract Base Class" (reproduced below) PSS ID Number: Q103983 Article last modified on 10-12-1995 1.00 1.50 1.51 1.52 | 1.00 2.00 2.10 WINDOWS | WINDOWS NT ---------------------------------------------------------------------- The information in this article applies to: - The Microsoft Foundation Classes (MFC), included with: - Microsoft C/C++ version 7.0 - Microsoft Visual C++ for Windows, versions 1.0, 1.5, 1.51, and 1.52 - Microsoft Visual C++ 32-bit Edition, version 1.0, 2.0, and 2.1 ---------------------------------------------------------------------- SUMMARY ======= An abstract base class is a class that contains one or more pure virtual functions. Attempting to use the IMPLEMENT_SERIAL() macro with an abstract base class results in the compiler error: error C2259 : illegal attempt to instantiate abstract class With Microsoft Visual C++ 32-bit edition, version 2.x, this compiler error is accompanied by an explanatory warning: error C2259: cannot instantiate abstract class due to following members: warning C4259: pure virtual function was not defined To work around this problem, implement the Serialize() function as normal for the abstract base class, and classes derived from the abstract base class, but skip over the abstract base class when using the IMPLEMENT_SERIAL() and DECLARE_SERIAL() macros. MORE INFORMATION ================ The IMPLEMENT_SERIAL() and DECLARE_SERIAL() macros declare a Construct() function, which creates an instance of the class. This function is used to create a new instance of the class when an object is read from a CArchive. Because C++ does not allow you to create instances of an abstract base class, the compiler error mentioned above is given when attempting to implement the construct function. However, because an object of the abstract base class cannot be created, it is not possible that such an object was ever stored in an archive. Only objects derived from the abstract base class need to be capable of serialization. To serialize a class derived from an abstract base class, do not use the IMPLEMENT_SERIAL() and DECLARE_SERIAL() macros with an abstract base class. Instead, just declare the Serialize() function and implement it as normal so that it will serialize the data in the abstract base class. When you derive classes from the abstract base class, use the IMPLEMENT_SERIAL() and DECLARE_SERIAL() macros. In the IMPLEMENT_SERIAL() macro, specify the base class of the abstract base class as the base class. In the Serialize() function of the derived class, serialize the data in the class and then call the base class version of Serialize() in the abstract base class so that data in the base class is serialized. For an example of the technique, please see the BLOCKS sample (S13483) in the Microsoft Software Library. Additional reference words: kbinf 1.00 1.50 2.00 2.10 2.50 2.51 2.52 3.00 3.10 KBCategory: kbprg KBSubcategory: MfcFileIO ============================================================================= Copyright Microsoft Corporation 1995.
| Вернуться в корень Архива |