15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


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.




| Вернуться в корень Архива |