分享

Serialization

 香猫猫 2010-08-15
 
[Brandon University crest][The famous "Halfway Tree" of the Prairies, between Winnipeg and Brandon]

Serialization 

Course Map
62:368 Home
Course Outline
Assignments

Readings

Resources

 

1         Introduction

MFC maintains data structures to manage information about modules, dynamic link libraries and classes at runtime. This paper describes how MFC uses these data structures. It also describes how a sample application program uses MFC and its own data structures to manage classes and dynamic link libraries.

2         Modules, Dynamic Link Libraries, and CRuntimeClass

A module is a runtime instance of a loadable entity. A loadable entity is an executable or a dynamic link library. A process consists of a number of loadable entities and the instance data for each.

AFX_MODULE_STATE is a class that contains information about resources used by a process. AFX_MODULE_STATE is comparable to a process-control block, except that it resides entirely in the application address space instead of in kernel space. Among other items, AFX_MODULE_STATE contains a list of serializable CRuntimeClass objects (those defined in the executable) as well as a list of CDynLinkLibrary objects which themselves each contain a list of serializable CRuntimeClass objects (those defined in each dll).

When a library is loaded at runtime, an entry is added to the list of CDynLinkLibrary objects to describe the runtime classes provided by the library.

A reference to the AFX_MODULE_STATE instance associated with a process can be retrieved by the global function AfxGetModuleState. (This function actually retrieves an instance of a thread-specific data structure and extracts from it the reference to the module state.)

class AFX_MODULE_STATE : public CNoTrackObject

{

public:

      ...

      // runtime class data

      CRuntimeClass* m_pClassInit;

      CTypedSimpleList<CRuntimeClass*> m_classList;

      ...

      CTypedSimpleList<CDynLinkLibrary*> m_libraryList;

      ...

};

CRuntimeClass

CRuntimeClass contains information about a class. There is a hierarchy of capabilities that support interrogation, creation, and serialization activities. A level of runtime capability is associated with a class by including a declaration in the class definition and in the implementation file (see below). This discussion is concerned with the highest level of capability: serialization.

For every serializable class definition in an exe or dll, there is one static instance of a CRuntimeClass object. Serializable CRuntimeClass objects contain two organizing links.

span style="font:7.0pt "Times New Roman"">        One set of links replicates the static class-derivation relationships. That is, each CRuntimeClass object contains a link to the CRuntimeClass object representing its base class. This link is used to answer runtime questions like: “is a an instance of A” and “is a an instance of B where B is a base class of A

span style="font:7.0pt "Times New Roman"">        The other set of links chains together all CRuntimeClass objects. Together with the name field of a CRuntimeClass, this linked list allows an answer to the question: “What is the address of the CRuntimeClass object whose class name is ‘CMyClass’?”

In addition to the organizing links, CRuntimeClass objects contain a string representing the name of the class, the size of an instance of the class, and a pointer to a function (a factory method) that can create an instance of the class using the default, parameterless constructor.

Construction of m_classList

A class is made serializable by adding the macro DECLARE_SERIAL(class_name) to the definition of the class and adding the macro IMPLEMENT_SERIAL(class_name, baseclass, version) to the implementation of the class.

DECLARE_SERIAL(CSomeClass) has the effect of adding the following declarations to the class definition.

public:

      static AFX_DATA CRuntimeClass classCSomeClass;

      virtual CRuntimeClass* GetRuntimeClass() const;

      static CObject* PASCAL CreateObject();

      AFX_API friend CArchive& AFXAPI operator>>(

CArchive& ar, CSomeClass* &pOb);

This has the following effect

span style="font:7.0pt "Times New Roman"">        A static member named classCSomeClass of type CRuntimeClass is added to the definition of CSomeClass. Because it is static, there is only one instance of classCSomeClass in the module, common to all instances of CSomeClass.

span style="font:7.0pt "Times New Roman"">        The virtual function GetRuntimeClass provides a polymorphic way of acquiring the runtime class of an instance, without referring directly to the nonpolymorphic classCSomeClass member.

span style="font:7.0pt "Times New Roman"">        The expression CSomeClass::CreateObject() is equivalent to the expression new CSomeClass() (except that the former returns CObject * whereas the latter returns CSomeClass *). Being static and therefore not bound to an instance, a reference to CreateObject can be stored in another data structure.

span style="font:7.0pt "Times New Roman"">        An archive extraction operator, strongly typed to CSomeClass, is added.

RUNTIME_CLASS(name) evaluates to the address of the static CRuntimeClass member of name. This has the following effects:

span style="font:7.0pt "Times New Roman"">        Because the CRuntimeClass member is static, name could be either an instance or a class name. That is, for the declaration CSomeClass instance, the expressions RUNTIME_CLASS(instance) and RUNTIME_CLASS(CSomeClass) evaluate to the same value.

span style="font:7.0pt "Times New Roman"">        Both RUNTIME_CLASS(instance) and RUNTIME_CLASS(CSomeClass) are manifest expressions.

CRuntimeClass has the following definition:

struct CRuntimeClass

{

      LPCSTR m_lpszClassName;

      int m_nObjectSize;

      UINT m_wSchema;

      CObject* (PASCAL* m_pfnCreateObject)();

            CRuntimeClass* m_pBaseClass;

      CObject* CreateObject();

      BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;

      void Store(CArchive& ar) const;

      static CRuntimeClass* PASCAL Load(

CArchive& ar, UINT* pwSchemaNum);

      CRuntimeClass* m_pNextClass;      

};

Note two important points: CRuntimeClass is declared as a struct instead of a class, and the first six members are variable. This allows an instance of CRuntimeClass to be initialized using a struct literal. (See IMPLEMENT_SERIAL, below.) And, since CRuntimeClass members are static, such members can be initialized at compile-time provided all expressions in the struct literal are manifest. Note, however, that m_pNextClass is not initialized in this way.

IMPLEMENT_SERIAL(CSomeClass, CBaseClass, 1) has the effect of adding the following definitions to the implementation file of CSomeClass

      CObject* PASCAL CSomeClass::CreateObject()

            { return new CSomeCLass; }

      AFX_DATADEF CRuntimeClass CSomeClass::classCSomeClass = {

            "CSomeClass", sizeof(class CSomeClass), 1,

CSomeClass::CreateObject,

            RUNTIME_CLASS(base_class_name), NULL };

      CRuntimeClass* CSomeClass::GetRuntimeClass() const

            { return RUNTIME_CLASS(CSomeClass); }

      AFX_CLASSINIT _init_CSomeClass(RUNTIME_CLASS(CSomeClass));

      CArchive& AFXAPI operator>>(CArchive& ar, CSomeClass* &pOb) {

   pOb = (CSomeClass*)ar.ReadObject(RUNTIME_CLASS(CSomeClass));

         return ar; }

The return type of _init_CSomeClass above is given as AFX_CLASSINIT. This is a struct with the following definition.

struct AFX_CLASSINIT {

AFX_CLASSINIT(CRuntimeClass* pNewClass) {

AfxClassInit(pNewClass);

}

};

The definition of AfxClassInit shows that the runtime class passed as a parameter is linked into the class list of the module state.

void AFXAPI AfxClassInit(CRuntimeClass* pNewClass)

{

      AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

      AfxLockGlobals(CRIT_RUNTIMECLASSLIST);

      pModuleState->m_classList.AddHead(pNewClass);

      AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);

}

AfxClassInit is called when a loadable entity is loaded. (Why? How?) There is a call to AfxClassInit for each serializable class. Thus all serializable classes are linked into the classlist of the module.

Dynamic Link Libraries and Serializable Classes

Each extension dll has a structure declared as

struct AFX_EXTENSION_MODULE

{

      BOOL bInitialized;

      HMODULE hModule;

      HMODULE hResource;

      CRuntimeClass* pFirstSharedClass;

      COleObjectFactory* pFirstSharedFactory;

};

The first two members are initialized to NULL.

When the dll is loaded a new instance of CDynLinkLibrary is allocated on the heap, passing the structure above as a parameter to the constructor.

CDynLinkLibrary is declared as

class CDynLinkLibrary : public CCmdTarget

{

      DECLARE_DYNAMIC(CDynLinkLibrary)

public:

      CDynLinkLibrary(AFX_EXTENSION_MODULE& state, BOOL bSystem = FALSE);

      CDynLinkLibrary(HINSTANCE hModule, HINSTANCE hResource);

      HMODULE m_hModule;

      HMODULE m_hResource

      CTypedSimpleList<CRuntimeClass*> m_classList;

      CTypedSimpleList<COleObjectFactory*> m_factoryList;

      BOOL m_bSystem;

public:

      CDynLinkLibrary* m_pNextDLL

      virtual ~CDynLinkLibrary();

};

The constructor for CDynLinkLibrary links the instance into the list of libraries in AFX_MODULE_STATE. The destructor unlinks the instance. Also, when the dll is loaded, AfxClassInit is called for all serializable classes in the dll.

All of the runtime classed can be traversed using AfxDoForAllClasses.

void AFXAPI AfxDoForAllClasses(void (AFX_CDECL *pfn)(const CRuntimeClass*, void*),

      void* pContext)

{

      AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

      AfxLockGlobals(CRIT_RUNTIMECLASSLIST);

      for (CRuntimeClass* pClass = pModuleState->m_classList; pClass != NULL;

            pClass = pClass->m_pNextClass)

      {

            (*pfn)(pClass, pContext);

      }

      AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);

      AfxLockGlobals(CRIT_DYNLINKLIST);

      for (CDynLinkLibrary* pDLL = pModuleState->m_libraryList; pDLL != NULL;

            pDLL = pDLL->m_pNextDLL)

      {

            for (pClass = pDLL->m_classList; pClass != NULL;

                  pClass = pClass->m_pNextClass)

            {

                  (*pfn)(pClass, pContext);

            }

      }

      AfxUnlockGlobals(CRIT_DYNLINKLIST);

}

The class list for the executable is traversed first, followed by the class list for each dll. A similar traversal is used when the deserialization algorithm loads a class from an archive.

Questions

1.      How is AfxInitClass called? For each serializable class, there is a function _init_##class_name which adds the class. Possibly the _init_ portion of the class name has some magic meaning to the compiler.

2.      AfxInitClass links the runtime class into the class list of the AFX_MODULE_STATE structure. But the traversal routine shows the lists for dlls to be rooted in the CDynLinkLibrary structure. How do serializable classes in dlls get added to this structure?

 Macros

If param is a macro parameter matching an argument xxx, the notation yyy##param is replaced  by the string yyyxxx.

Also, the notation #param is replaced by the string “xxx” (including the quotation marks), thus turning a macro parameter into a string literal.

 

Last update 03/18/03
Copyright ?Gerald Dueck
[=]

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多