Marshalling C++ Objects with XML

Marshalling allows a developer to represent C++ objects in XML text format that can be sent over the Net and demarshalled into C++ objects on the recipient site. Konstantin presents a high-performance and flexible marshalling subsystem.


April 01, 2002
URL:http://drdobbs.com/marshalling-c-objects-with-xml/184416422

April 2002/Marshalling C++ Objects with XML

Marshalling is a technique that allows a developer to represent C++ (or Java) objects in XML text format. The XML text can be transferred and demarshalled into C++ object on the recipient site. The advantages of marshalling are described in “Professional XML, Second Edition, Chapter 15, XML Data Binding”. Marshalling is actually another kind of serialization. However, marshalling is much more powerful and flexible and can be implemented so that its performance will be nearly as high as with serialization.

Recently I was involved in a client-server project were we used marshalling for transferring C++ objects between remote peers. In the beginning I looked for publicly available code but didn’t find any high performance and flexible code for marshalling of C++ objects. So I decided to develop it on my own. The result of my developments is a marshalling subsystem with the following features:

The source code is available online, and it consists of the three parts: the standard XML parser (expat — see www.jclark.com/xml/expat.html), the test program with various examples (cppmarsh.cpp, simple.*, ...), and the marshalling core (xmlbind.*, xmlprocessor.*).

Marshalling and Unmarshalling of Simple C++ Objects

Let us consider how to convert an existing C++ object into a marashallable object:

struct  CSimple {
    int cnt;
    string name;
    float pi;
};

Step 1. Derive the object from the Marshallable interface and insert DECLARE_MARSHALING string into the object declaration:

struct    CSimple : public Marshallable {
    int count;
    string name;
    float pi;
DECLARE_MARSHALING(CSimple)
};

Step 2. Define data description in cpp file:

BEGIN_IMPLEMENT_MARSHALING(CSimple)
    XDAT(int,     count,  "Count")
    XDAT(string,  name,   "Name")
    XDAT(float,   pi,     "PI")
END_IMPLEMENT_MARSHALING()

Step 3. To convert your object into XML representation, call the Marshall method:

CSimple obj;
obj.cnt = 100;
obj.name = "test for simple object";
obj.pi = 3.14;

XMLDoc* pDoc = NULL;
obj.Marshal(pDoc);
const char* xmltext = pXMLDoc->ReleaseText();

printf("%s\n", xmltext);

the result is as follows:

<CSimple>
    <Count>100</Count>
    <Name>test for simple object</Name>
    <PI>3.14</PI>
</CSimple>

Step 4. To convert the XML text into the object use XMLParser:

string message;
...
XMLParser parser;
Marshallable* pObj = parser.Parse(message);

The pointer to object is not equal to zero if the parser was able to parse the XML string.

Step 5. Use the Marshallable interface methods to obtain type name and typeid:

printf("Object type name: %s\n", pObj->GetTypeName());
printf("Object type id: %x\n pObj->GetTypeId());

the result is as follows:

Object type name: CSimple
Object type id: 0x5AA8AF35

The type identifier is an unsigned 32-bit integer that is calculated from the type name string by the standard CRC32 algorithm:

CRC32("CSimple") = 0x5AA8AF35

The GetTypeName and GetTypeId functions allow a developer to find an object’s type from the pointer to the Marshallable class. In the previous example, a developer can check the type name:

if(string("CSimple") == pObj->GetTypeName())

The real application may handle tens or hundreds of different marshallable types. In this case, it is convenient to use the GetTypeId function, for example:

switch(pObj->GetTypeId())
{
case tsig_CSimple:
    CSimple* pSimple = (CSimple*)pObj;
    ...;
break;
...
}

where tsig_CSimple is a constant defined by the developer in a header file as:

#define tsig_CSimple    (0x5AA8AF35)

I developed a utility program called “Type Signature Calculator” that helps me to calculate a type identifier constant for a marshallable type, see Figure 1.

A Self-Describing Implementation

The method is based on the idea of adding a self-description capability to a C++ class, which unlike Java and some other modern programming languages, does not support self-descriptive types. Class self-description should be added by developer via use of the XDAT macro for marshalled data members. Format of XDAT macro is simple:

XDAT(type,    member,    name)

In this format, type is a class member type, member is a class member name, and name is the name of the XML element. In most cases, XML element name and class member name coincide. They may differ if the developer wants them to. This opens the possibility for automatic generation of class self-description by using of a simple tool that creates XDAT table from a class definition.

The XDAT macro is defined in xmlbind.h as follows:

#define XDAT(type,field,fnm)
    new _ElementDescription(#type, 0,
        ((int)&(((_alias*)0)->field)), 
        sizeof(type), 1,
        fnm, marshal_##type, unmarshal_##type,
        ANY_ASP),

Another important macro XATR can be used to represent a class member not as an XML element but as an attribute. Format of the XATR macro is the same as that of XDAT. For example, we can specify a types description table for CSimple class as follows:

BEGIN_IMPLEMENT_MARSHALING(CSimple)
    XATR(int,     count,  "Count")
    XATR(string,  name,   "Name")
    XDAT(float,   pi,     "PI")
END_IMPLEMENT_MARSHALING()

where the difference with the previous example is that we use XATR for the two first parameters instead of XDAT. The result of marshalling will be as follows:

<CSimple Count="100" Name="test for simple object" >
    <PI>3.14</PI>
</CSimple>

There is an important advantage to represent a class data member as an XML attribute: attributes are available to the marshalling subsystem at the beginning of the unmarshalling of the class. Attributes are used to support partial updates of objects as described below in the “Differences publishing” paragraph.

The marshalling subsystem is capable of marshalling the basic and STL types listed in Table 1. Marshalling of other types can be implemented analogously to these standard types. For this, you can copy and modify the marshal_type sample procedure from xmlbind.cpp for each new type.

Marshalling of Complex C++ Objects

Some C++ classes can be complex: a class member can be a class itself, a pointer to linked list, an array, and so on. Here, I consider how different complex members are marshalled. In the first example, the class member is a class or a pointer to a class.

class A : public Marshallable {
public:
    int a1;
    string a2;
DECLARE_MARSHALING(A)
};

class B : public Marshallable {
public:
    WORD b1;
    A* b2;
DECLARE_MARSHALING(B)
};

You can use the standard PMARSH pseudo-type as follows:

BEGIN_IMPLEMENT_MARSHALING(B)
    XDAT(WORD,    b1,  "b1")
    XDAT(PMARSH,  b2,  "b2")
END_IMPLEMENT_MARSHALING()

The result of marshalling will be:

<B>
    <b1>123</b1>
    <A>
        <a1>56</a1>
        <a2>the test</a2>
    </A>
</B>

There is no type PMARSH defined in any header file. However, PMARSH word is used to specify that the marshalling subsystem should use the marshal_PMARSH procedure to marshal a marshallable class. The marshal_PMARSH procedure is implemented in xmlbind.cpp and is shown in Example 1.

In our next example, the class member is a pointer to the head of the linked list.

// Node.h
class Node : public Marshallable {
public:
    int data;
    Node* next;
DECLARE_MARSHALING(Node)
};

// Container.h
class Container : public Marshallable {
public:
    string smth;
    Node* listhead;
DECLARE_MARSHALING(Container)
};

You should create a custom marshalling procedure for type Node by copying and modifying the marshal_type sample procedure from xmlbind.cpp. In the Container class description table, you use the following macro:

XDAT(Node,    listhead,   "NodeSList")

The marshal_Node procedure can be implemented as shown in Example 2. The XMLDoc, XMLElement, and ElementDesc are types defined in the xmlbind.h header file, see Listing 1.

Publishing Differences

The second parameter in the Marshal procedure is optional and can be used for publishing differences:

    CSimple objnew, objold;
    ...
    objnew.Marshal(&XMLDoc, &objold);

The marshalling subsystem will automatically determine which members of obj1 and obj2 differ and publish only those. This powerful technique can be used to propagate only changed data members in an object. For example, the server often sends updates of an object to the client. It makes sense to send only a single data member if only one was changed and not to send the dozens that were not changed. Each time the object is about to change, the server uses the old state and the new state of the object to propagate change as shown previously. You should implement the CreateInstance function, which is the part of the Marshallable interface for class Csimple, see Example 3. Pseudo-code search_in_collection can find an existing object based on attributes.

Marshalling a Class Aspect

The Marshalling subsystem supports grouping of class members. Each group is an aspect of the class. It’s convenient to marshal one, several, or all aspects of a class. Currently, up to 32 aspects can be declared for a class. Also, each class member can be part of one or more aspects. The XDAT macro marks the data member of a class as part of any aspect. The XASP macro is analogous to XDAT but it has an extra parameter — the identifier of the aspect group. You should define an aspect identifier explicitly in code, for example:

    #define NAME_ASPECT        1
    #define AGE_ASPECT         2
    #define ADDRESS_ASPECT     4

Strictly speaking, the aspect identifier number has to be a power-of-two integer. The exception to this rule is only the ANY_ASPECT constant, which is declared in xmlbind.h as (0xFFFFFFFF).

struct    AnswerList : public Marshallable
    {
        string last_name;
        string first_name;
        string address;
        int age;

        DECLARE_MARSHALLING(AnswerList)
};

Use the XASP macro instead of XDAT in a cpp file, as shown in Example 4. Use the Marshal method to publish the aspect only:

AnswerList lst;
lst.Marshal(&XMLDoc, 0, NAME_ASPECT);

Role of Serialization

For marshalling of large objects with hundreds of data members, you may use serialization, which provides some advantages. It’s faster; and having the ability to serialize does not require you to define all object members with XDAT or XASP macros, which saves development time. You can use an ancillary stub class to transfer serialized representation of a class, see Example 5.

Object Embedding

You can use the object embedding technique when transferring objects between remote peers. The main idea of embedding is to use a universal envelope to “wrap” the transferred object: CSrvEnvelope. Every transferrable class should have the ability to “insert” itself into an envelope in the CreateInstance procedure shown in Example 6.

The importance of using the envelope class is high also because it allows a developer to specify destination, source, and some additional information.

Performance Estimations

I conducted some performance estimations for marshalling C++ objects with different number of data members ranging from two elements up to 80 elements. The results are shown in Table 2 and are graphed in Figure 2. The computer used featured an Athlon 1.4GHz CPU and 512 MB of RAM.


Konstantin Izmailov holds a Ph.D. in Computer Science. He is currently a Senior Software Engineer at Hypercom USA Inc. and can be reached at [email protected].

April 2002/Marshalling C++ Objects with XML

Figure 1: Utility to calculate a type identifier constant

April 2002/Marshalling C++ Objects with XML

Figure 2: Marshalling subsytem performance

April 2002/Marshalling C++ Objects with XML

Listing 1: xmlbind.h
The main header file for XML Binding Technology

/*    Xmlbind.h

    This is a main header file for XML Binding Technology. The XML Binding is a method
    to convert C++ objects into XML text representation and vice versa.

    The idea of method is described below.

    Since C++ language has no means for an object to describe itself it is necessary 
    to explicitly define description table for each object type that you wish to 
    marshal. The object (or one of its parent) should be derived from Marshallable 
    interface and DECLARE_MARSHALING macro should be added into public section of the 
    class. Please refer to program examples to learn details of the XML Binding usage.

    Current implementation is using following assumptions for performance reasons:
    1. Maximum length of the XML representation of an object does not 
    exceed XML_DEFAULT_BUFSIZE (300K).
*/
#ifndef _XMLBIND_H_
#define _XMLBIND_H_

#include <windows.h>    // for data types definition

#pragma warning (disable:4786)
#include <sstream>
#include <string>
#include <list>
#include <map>
#include "crc32.h"

using namespace std;

#define ANY_ASP        (0xFFFFFFFF)

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// G L O B A L   T Y P E S   R E G I S T R Y   D E F I N I T I O N

class XMLDoc;
class XMLElement;
class Marshallable;
class _ElementDescription;

//////////////////////////////////////////////////////////////////
// Elementary marshalling function pointer types
//////////////////////////////////////////////////////////////////
typedef void (*marshal_proc_t)(XMLDoc* pDoc, XMLElement* pElem, const 
_ElementDescription* pet, void* pdata, void* pother, int
aspect); typedef void (*unmarshal_proc_t)(void* obj, const _ElementDescription* pElem,
const char *ch, int length); ////////////////////////////////////////////////////////////////// // Instance handling function pointer type ////////////////////////////////////////////////////////////////// typedef Marshallable* (*_f_CreateInstance)(const char **atts, void *parent = 0); ////////////////////////////////////////////////////////////////// // // _Reg_TypeDescHelper // // Ancillary class used to keep a class description and other // information in a global registry. // ////////////////////////////////////////////////////////////////// class _Reg_TypeDescHelper { public: _Reg_TypeDescHelper() : m_ppElemDesc(0), m_procCreateInstance(0), m_signature(0) {} _Reg_TypeDescHelper(const _ElementDescription** arg_et, _f_CreateInstance
arg_CreateInstanceProc, const char* name) : m_ppElemDesc(arg_et), m_procCreateInstance(arg_CreateInstanceProc) { m_signature = CRC32().exec(name, strlen(name)); } const _ElementDescription** m_ppElemDesc; _f_CreateInstance m_procCreateInstance; DWORD m_signature; // 32-bit number that uniquely identifies the type }; ////////////////////////////////////////////////////////////////// // // _Reg_TypeNameToRefDescriptionMap // // Map type which maps a class name into the class description. // ////////////////////////////////////////////////////////////////// typedef map<string, _Reg_TypeDescHelper> _Reg_TypeNameToRefDescriptionMap; ////////////////////////////////////////////////////////////////// // // _Reg_GlobalTypesRegistry // // Singleton class that represents global types registry. // Due to this registry unmarshalling becomes possible. // ////////////////////////////////////////////////////////////////// class _Reg_GlobalTypesRegistry { public: static _Reg_TypeNameToRefDescriptionMap& Instance(); static void Destroy(); protected: _Reg_GlobalTypesRegistry(); private: static _Reg_TypeNameToRefDescriptionMap* inst; }; ////////////////////////////////////////////////////////////////// // // _Reg_Registrator // // Ancillary class used to register _Reg_TypeDescHelper of a praticular class // in the global registry. // ////////////////////////////////////////////////////////////////// class _Reg_Registrator { public: _Reg_Registrator(const char* name, const _ElementDescription** ppElemDesc,
_f_CreateInstance m_procCreateInstance) { _Reg_GlobalTypesRegistry::Instance()[name] = _Reg_TypeDescHelper(ppElemDesc,
m_procCreateInstance, name); } }; ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // M A R S H A L L A B L E I N T E R F A C E D E F I N I T I O N ////////////////////////////////////////////////////////////////// // // _ElementDescription // // Ancillary class used to describe a class data member. // ////////////////////////////////////////////////////////////////// class _ElementDescription { public: WORD offset; // offset in bytes to the data field WORD size; // size in bytes of the data field char *field_name; // field name unsigned field_name_len; // field name length (for better marshalling performance) marshal_proc_t marshal; unmarshal_proc_t unmarshal; int count; // equals to 1 for single elements; used to support arrays of elements int aspect; // either ANY_ASP (for any aspect) or aspect mask int attrib; // 0-simple data, 1-attribute, 2-complex data _ElementDescription(const char* arg_info, int arg_attrib, WORD arg_offset,
WORD arg_size, int arg_count, char *arg_field_name,
marshal_proc_t marshal_proc, unmarshal_proc_t unmarshal_proc,
int arg_asp) : offset(arg_offset), size(arg_size), count(arg_count),
field_name(arg_field_name), marshal(marshal_proc),
unmarshal(unmarshal_proc), attrib(arg_attrib), aspect(arg_asp) { field_name_len = strlen(field_name); } }; ////////////////////////////////////////////////////////////////// // // _RefToElementDescription // // Ancillary class used to link to ElementDescription array // of the marshallable class and to link to the base class if any. // ////////////////////////////////////////////////////////////////// struct _RefToElementDescription { const _RefToElementDescription* m_pBaseElemDesc; _ElementDescription** m_ppElemDesc; }; ////////////////////////////////////////////////////////////////// // // Marshallable // // Abstract base class used as a parent to a C++ class. // ////////////////////////////////////////////////////////////////// class Marshallable { public: // Constructors/destructors virtual int Marshal(XMLDoc*& pDoc, const void* other = 0, XMLElement*
pDocElem = 0, int aspect = ANY_ASP); virtual ~Marshallable() {} // Abstract interface functions virtual const _RefToElementDescription* GetElementsTable() = 0; virtual const char* GetTypeName(int aspect = ANY_ASP) const = 0; virtual DWORD GetTypeId(int aspect = ANY_ASP) const = 0; }; ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // C O R E M A C R O S D E F I N I T I O N #define DECLARE_MARSHALING(type_name) \ static Marshallable* CreateInstance(const char **atts, void *parent = 0); \ virtual DWORD GetTypeId(int aspect = ANY_ASP) const \ { \ map<int, string>::const_iterator it = (*_sm_pAspectMap).find(aspect); \ if(it == (*_sm_pAspectMap).end()) \ return 0; \ \ _Reg_TypeNameToRefDescriptionMap::const_iterator it2 =
_Reg_GlobalTypesRegistry::Instance().find((*it).second); \ \ if(it2 != _Reg_GlobalTypesRegistry::Instance().end()) \ { \ const _Reg_TypeDescHelper& th = (*it2).second; \ return th.m_signature; \ } \ return 0; /* not found */ \ } \ \ virtual const char* GetTypeName(int aspect = ANY_ASP) const \ { \ map<int, string>::const_iterator it = (*_sm_pAspectMap).find(aspect); \ return (it == (*_sm_pAspectMap).end()) ? "unknown" : (*it).second.c_str(); \ } \ \ virtual const _RefToElementDescription* GetElementsTable() \ { \ return &_sm_refToElemDesc; \ } \ \ /* Static data members for ancillary use */ \ static _ElementDescription* _sm_ElementsDescriptionTbl[]; \ static _RefToElementDescription _sm_refToElemDesc; \ static map<int, string>* _sm_pAspectMap; \ \ static map<int, string>* getAspectMap() \ { \ if(0 == _sm_pAspectMap) \ _sm_pAspectMap = new map<int, string>; \ \ return _sm_pAspectMap; \ } #define BEGIN_IMPLEMENT_MARSHALING(class_name) \ IMPLEMENT_ASPECT(class_name, ANY_ASP, #class_name) \ static _Reg_Registrator _r_##class_name (#class_name, (const _ElementDescription**)
&(class_name::_sm_ElementsDescriptionTbl[0]), (_f_CreateInstance)
class_name::CreateInstance);\ map<int, string>* class_name::_sm_pAspectMap = 0; \ _RefToElementDescription class_name::_sm_refToElemDesc = { 0,
&class_name::_sm_ElementsDescriptionTbl[0] }; \ _ElementDescription* class_name::_sm_ElementsDescriptionTbl[] = { #define BEGIN_IMPLEMENT_MARSHALING2(class_name, base_class) \ IMPLEMENT_ASPECT(class_name, ANY_ASP, #class_name) \ static _Reg_Registrator _r_##class_name (#class_name, (const _ElementDescription**) &(class_name::_sm_ElementsDescriptionTbl[0]), (_f_CreateInstance)
class_name::CreateInstance);\ map<int, string>* class_name::_sm_pAspectMap = 0; \ _RefToElementDescription class_name::_sm_refToElemDesc = {
&base_class::_sm_refToElemDesc, &class_name::_sm_ElementsDescriptionTbl[0] }; \ _ElementDescription* class_name::_sm_ElementsDescriptionTbl[] = { #define IMPLEMENT_ASPECT(class_name, asp, fnm) static bool class_name##asp =
((*class_name::getAspectMap())[asp] = fnm, false); #define XDAT(type,field,fnm) new _ElementDescription(#type, 0,
((int)&(((_alias*)0)-> field)), sizeof(type), 1,
fnm, marshal_##type, unmarshal_##type, ANY_ASP), #define XATR(type,field,fnm) new _ElementDescription(#type, 1,
((int)&(((_alias*)0)-> field)), sizeof(type), 1,
fnm, marshal_##type, unmarshal_##type, ANY_ASP), #define XCUSTOM(type,field,fnm) new _ElementDescription(#type, 2,
((int)&(((_alias*)0)-> field)),
sizeof(((_alias*)0)-> field), 1, fnm,
marshal_##type, 0, ANY_ASP), #define XASP(type,field,fnm,asp) new _ElementDescription(#type, 0,
((int)&(((_alias*)0)-> field)), sizeof(type), 1,
fnm, marshal_##type, unmarshal_##type, asp), // array of elementary variables (e.g. char[], int[], float[], string[]) #define XARR(type,size,field,fnm) new _ElementDescription(#type, 4,
((int)&(((_alias*)0)-> field)), sizeof(type),
size, fnm, marshal_##type, unmarshal_##type,
ANY_ASP), #define END_IMPLEMENT_MARSHALING() 0, (_ElementDescription*) _alias ::getAspectMap() }; ////////////////////////////////////////////////////////////////// // // _XMLObject // ////////////////////////////////////////////////////////////////// class _XMLObject { public: const _RefToElementDescription& typmap; void* obj; const _ElementDescription* field; // current field pointer; used when
// parsing an element _XMLObject(const _RefToElementDescription* arg_typmap, void* arg_obj) :
typmap(*arg_typmap), obj(arg_obj), field(0) {} const _ElementDescription* FindField(const char *name) const; }; ////////////////////////////////////////////////////////////////// // Marshaling utilities for basic types ////////////////////////////////////////////////////////////////// void marshal_int(XMLDoc* pDoc, XMLElement* pElem, const _ElementDescription* pet,
void* pdata, void* pother, int aspect); void marshal_bool(XMLDoc* pDoc, XMLElement* pElem, const _ElementDescription* pet,
void* pdata, void* pother, int aspect); void marshal_char(XMLDoc* pDoc, XMLElement* pElem, const _ElementDescription* pet,
void* pdata, void* pother, int aspect); void marshal_float(XMLDoc* pDoc, XMLElement* pElem, const _ElementDescription* pet,
void* pdata, void* pother, int aspect); void marshal_double(XMLDoc* pDoc, XMLElement* pElem, const _ElementDescription* pet,
void* pdata, void* pother, int aspect); void marshal_pchar(XMLDoc* pDoc, XMLElement* pElem, const _ElementDescription* pet,
void* pdata, void* pother, int aspect); void marshal_BYTE(XMLDoc* pDoc, XMLElement* pElem, const _ElementDescription* pet,
void* pdata, void* pother, int aspect); void marshal_WORD(XMLDoc* pDoc, XMLElement* pElem, const _ElementDescription* pet,
void* pdata, void* pother, int aspect); void marshal_string(XMLDoc* pDoc, XMLElement* pElem, const _ElementDescription* pet,
void* pdata, void* pother, int aspect); void unmarshal_int(void* obj, const _ElementDescription* pElem, const char *ch, int length); void unmarshal_bool(void* obj, const _ElementDescription* pElem, const char *ch, int length); void unmarshal_char(void* obj, const _ElementDescription* pElem, const char *ch, int length); void unmarshal_float(void* obj, const _ElementDescription* pElem, const char *ch, int length); void unmarshal_double(void* obj, const _ElementDescription* pElem, const char *ch, int length); void unmarshal_pchar(void* obj, const _ElementDescription* pElem, const char *ch, int length); void unmarshal_BYTE(void* obj, const _ElementDescription* pElem, const char *ch, int length); void unmarshal_WORD(void* obj, const _ElementDescription* pElem, const char *ch, int length); void unmarshal_string(void* obj, const _ElementDescription* pElem, const char *ch, int length); ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // O T H E R M A R S H A L L I N G H E L P E R C L A S S E S ////////////////////////////////////////////////////////////////// // // XMLElement // ////////////////////////////////////////////////////////////////// class XMLElement { friend class XMLDoc; public: XMLElement(){} ~XMLElement(); int setAttribute(const char* attr_name, unsigned nAtrLen, const char* value,
unsigned nValLen); XMLElement* appendElement(const char* pszTag, unsigned nTagLen, const char*
pszText = 0, unsigned nTextLen = 0); void* operator new(size_t stBlock, XMLDoc* pOwner, XMLElement* pParent, int
nBegOfs, int nTagLen); void operator delete(void*); static char m_Storage[5000]; // contains temporary XML Elements. protected: XMLDoc* m_pOwner; // owner document XMLElement* m_pParent; // parent element if any int m_nBegOfs; // beginning of the element int m_nAtrOfs; // end of the attributes int m_nTagLen; // tag length static char* m_Ptr; // pointer to last temporary XML Element content }; ////////////////////////////////////////////////////////////////// // // XMLDoc // ////////////////////////////////////////////////////////////////// class XMLDoc { friend class XMLElement; public: enum { XML_DEFAULT_BUFSIZE = 300*1024 }; XMLDoc(const char* szType); virtual ~XMLDoc(); XMLElement* getDocumentElement() { return &m_RootElem; } const char* ReleaseText(); protected: string m_sType; // root element name char *m_szBuf; // beginning of the intenal buffer unsigned m_nBufSize; // size of the internal buffer char *m_szPtr; // pointer where to insert new elements unsigned m_nTail; // length of the tail with useful data bool m_bReleased; // true if the content of the document was released XMLElement m_RootElem; // root element static char m_MainBuf[XML_DEFAULT_BUFSIZE]; // output of marshaling will be
// stored in the buffer inline void m_SetOpenTag(const char* pszTag, int nTagLen, bool bNewLine = false) { *m_szPtr++ = '<'; while(--nTagLen >= 0) { *m_szPtr++ = *pszTag++; } *m_szPtr++ = '>'; if(bNewLine) *m_szPtr++ = '\n'; } inline void m_SetCloseTag(const char* pszTag, int nTagLen) { *m_szPtr++ = '<'; *m_szPtr++ = '/'; while(--nTagLen >= 0) { *m_szPtr++ = *pszTag++; } *m_szPtr++ = '>'; *m_szPtr++ = '\n'; } inline void m_SetText(const char* pszText, int nTextLen) { while(--nTextLen >= 0) { *m_szPtr++ = *pszText++; } } inline void m_SetAttribute(const char* attr_name, const char* value) { *m_szPtr++ = ' '; while(*attr_name != 0) { *m_szPtr++ = *attr_name++; } *m_szPtr++ = '='; *m_szPtr++ = '"'; while(*value != 0) { *m_szPtr++ = *value++; } *m_szPtr++ = '"'; } inline void m_PlaceText(const char* pszText, int nTextLen) { char* szPtr = m_szPtr; while(--nTextLen >= 0) { *szPtr++ = *pszText++; } } inline void m_GetSpace(int nSpace) { // move tail if necessary if(m_nTail != 0) { memmove(m_szPtr+nSpace, m_szPtr, m_nTail); } } }; #endif

April 2002/Marshalling C++ Objects with XML

Table 1: Basic and STL types that can be marshalled

April 2002/Marshalling C++ Objects with XML

Table 2: Performance estimations for marshalling C++ objects

April 2002/Marshalling C++ Objects with XML

Example 1
The marshal_PMARSH function

// marshal a marshallable object
void marshal_PMARSH(XMLDoc* pDoc, XMLElement* pDocElem, const ElementDesc*, 
void* pdata, void* pother, int aspect)
{
    // pdata points to variable with pointer to Marshallable type
    if(0 == pDocElem)
        pDocElem = pDoc->getDocumentElement();

    Marshallable *pMarsh = *(Marshallable**)pdata;
    Marshallable *pOtherMarsh = (pother != 0) ? *(Marshallable**)pother : 0;

    XMLElement* pElem = pDocElem->appendElement("_RegisteredType_", 16);
    const char* type_name = pMarsh->GetTypeName(aspect);
    pElem->setAttribute("name", 4, type_name, strlen(type_name));

    pMarsh->Marshal(pDoc, pElem, pother, aspect);

    delete pElem;
}

April 2002/Marshalling C++ Objects with XML

Example 2
The marshal_Node function

void marshal_Node(XMLDoc* pDoc, XMLElement* pDocElem, const 
ElementDesc* pet, void* pdata, void* pother, int aspect) {
    for(Node* pNode=*(Node**)pdata; pNode!=0; pNode=pNode->next) {
        if(0 == pDocElem)
            pDocElem = pDoc->getDocumentElement();
        
        XMLElement* pElem = pDocElem->appendElement("Node", 4);

        pNode->Marshal(pDoc, pElem, pother, aspect);

        delete pElem;
    }
}

April 2002/Marshalling C++ Objects with XML

Example 3
The CreateInstance function

Marshallable* CSimple::CreateInstance(const char **atts, void *parent) {
    CSimple* pTemp = searsh_in_collection(atts);
    CSimple* pInst = (pTemp != 0) ? new CSimple(pTemp) : new CSimple;
    return pInst;
 }

April 2002/Marshalling C++ Objects with XML

Example 4
Using the XASP macro

    IMPLEMENT_ASPECT(AnswerList, NAME_ASPECT, "Participant Name");
    IMPLEMENT _ASPECT(AnswerList, AGE_ASPECT, "Participant Age");
BEGIN_IMPLEMENT_MARSHALING(AnswerList)
        XASP(string, last_name, "Last Name", NAME_ASPECT)
        XASP(string, first_name, "First Name", NAME_ASPECT)
    XASP(int, age, "Age", AGE_ASPECT)
    XASP(string, address, "Address", ADDRESS_ASPECT)
END_IMPLEMENT_MARSHALING()

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.