posts - 2, comments - 6, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

2007年11月21日

讲一点和实现细节相关的东西。在Visual C++中,所有在main之前执行的函数调用实际上都通过一个自动生成的函数来调用,比如下面这段代码:

int func()
{
 return 1;
}

int data = func();

int main()
{
 return 0;
}

实际上生成了三个函数:

?func@@YAHXZ,对应于 func
_main 对应于 main
_$E1 对应于 data=func() 这句赋值语句。它调用了 func,并且完成赋值这个操作。

窍门在于,VC将 _$E1这个函数的指针放到了段CRT$XCU中:

CRT$XCU SEGMENT
_$S2 DD FLAT:_$E1
; Function compile flags: /Odt /RTCsu /ZI
CRT$XCU ENDS

这个段的定义为:

CRT$XCU SEGMENT DWORD USE32 PUBLIC 'DATA'
CRT$XCU ENDS

参考 crt0dat.c 文件可以看到:

extern _CRTALLOC(".CRT$XIA") _PIFV __xi_a[];
extern _CRTALLOC(".CRT$XIZ") _PIFV __xi_z[];    /* C initializers */
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[];    /* C++ initializers */
extern _CRTALLOC(".CRT$XPA") _PVFV __xp_a[];
extern _CRTALLOC(".CRT$XPZ") _PVFV __xp_z[];    /* C pre-terminators */
extern _CRTALLOC(".CRT$XTA") _PVFV __xt_a[];
extern _CRTALLOC(".CRT$XTZ") _PVFV __xt_z[];    /* C terminators */

这里实际上有一个很巧妙的地方在于,VC应用了x86上段是连续并且可重叠的概念,因此CRT$XCU是位于CRT$XCA到CRT$XCZ之间,具体说,段的顺序是:

CRT$XCA SEGMENT DWORD USE32 PUBLIC 'DATA'
CRT$XCA ENDS
CRT$XCU SEGMENT DWORD USE32 PUBLIC 'DATA'
CRT$XCU ENDS
CRT$XCL SEGMENT DWORD USE32 PUBLIC 'DATA'
CRT$XCL ENDS
CRT$XCC SEGMENT DWORD USE32 PUBLIC 'DATA'
CRT$XCC ENDS
CRT$XCZ SEGMENT DWORD USE32 PUBLIC 'DATA'
CRT$XCZ ENDS

由于CRT$XCA开始都是C++初始化函数,_PVFV实际上就是 void (*_PVFV)(),因此CRT的_initterm()函数就把这个段中的数据作为一个函数指针数组来访问,依次调用其中的函数,从而完成系统所有初始化操作。

最后,也是最关键的问题,就是实际上每个 CPP 文件编译好以后都有初始化函数,并且其指针位于 CRT$XC? 段中,随后连接程序 LINK 做了最后一个重要的任务,就是把所有具有相同名字的段合并成为一个单独的段(这也就是连接程序名字的由来之一),合并的做法就是简单地把每个段中的数据按顺序前后放到一个连续的空间就可以了。这样在最终运行的时候,程序看到的CRT$XC?段也就是一个连续的数组,而不是多个数组。

至于顺序问题,在这里就可以看到,是由连接程序最后做拼接时候确定的。连接程序拼接的顺序,基本上是它看到OBJ文件的顺序,也就是在连接程序命令行指定的顺序。因此,在程序中决不能依赖于这个顺序,因为在连接程序命令行中的文件顺序是不确定的。

以上是初始化程序的顺序,至于析构函数(或者在main函数之后的函数调用)则是通过用at_exit函数注册的顺序来确定,而注册往往是在初始化的时候进行,因此析构函数的调用顺序也是不确定的。

 


 

posted @ 2007-11-21 16:31 Ling Xu 阅读(2583) | 评论 (2)编辑 收藏

XML 序列化器

最近试图编写一个C++的XML序列化器,目前实现了一些基本的功能。为了开发一个XML序列化器,首先要有XML读取器(Reader)和书写器(Writer)。我实现了两个简单的XML Reader和Writer:


 

    class SimpleXmlElement
    {
    public:
        enum NodeType
        {
            UNKNOWN,
            ATTRIBUTE,
            ELEMENT,
            ENDOFELEMENT
        };

    private:
        friend class SimpleXmlReader;

    public:
        NodeType GetType() const;
        const String & GetName() const;
        bool IsEmptyElement() const;

    protected:
        void Reset(NodeType type, const String & name, bool isEmptyElement);

    private:
        NodeType m_type;
        String m_name;
        bool m_isEmptyElement;
    };

    class SimpleXmlAttribute: public SimpleXmlElement
    {
        friend class SimpleXmlReader;

    public:
        const String & GetValue() const;

    protected:
        void Reset(const String & name, const String & val);

    private:
        String m_val;
    };

    class SimpleXmlReader
    {
    public:
        SimpleXmlReader();
        virtual ~SimpleXmlReader();

        virtual void Open(IStream * stream, ICharsetEncoding * encoding = NULL)
        virtual void Open(const void * buffer, SIZE_TYPE length, ICharsetEncoding * encoding = NULL);
        virtual void Open(const void * buffer, int offset, SIZE_TYPE length, ICharsetEncoding * encoding = NULL);

        virtual bool IsEndOfStream() const;
        virtual const SimpleXmlElement * GetNextElement();
        virtual const SimpleXmlAttribute * GetNextAttribute();
        virtual const String GetElementInnerText();

    };

    //-------------------------------------------------------------------------------------------
 
    class SimpleXmlWriter
    {
    public:
        SimpleXmlWriter();
        virtual ~SimpleXmlWriter();

        virtual void Open(ICharsetEncoding * encoding = NULL);
        virtual void Open(IStream * stream, ICharsetEncoding * encoding = NULL);

        virtual void EnterElement(const String & element);
        virtual void AddAttribute(const String & attribute, const String & value);
        virtual void SetElementInnerText(const String & text);
        virtual void LeaveElement();

        virtual const void * GetBuffer();
        virtual SIZE_TYPE GetLength();
    };


在这两个类中,IStream是保存数据的流接口,ICharsetEncoding是用于字符集转换的编码接口。

使用这两个类就可以进行XML序列化了。

 

    class XmlSerializer;

    struct IXmlSerializable
    {
        virtual void Serialize(XmlSerializer * serializer, bool serialize) = 0;
    };

    class XmlTree;

    class XmlSerializer
    {
    public:
        XmlSerializer();
        virtual ~XmlSerializer();

        virtual void OpenSerializer(IStream * stream, ICharsetEncoding * encoding = NULL);
        virtual void OpenDeserializer(IStream * stream, ICharsetEncoding * encoding = NULL);
        virtual void Close();

        virtual void SerializeElement(const String & className, const String & elementName, bool var);
        virtual void SerializeElement(const String & className, const String & elementName, int var);
        virtual void SerializeElement(const String & className, const String & elementName, const String & var);
        virtual void SerializeElement(const String & className, const String & elementName, IXmlSerializable & var);

        virtual void SerializeAttribute(const String & className, const String & elementName, const String & attributeName, bool var);
        virtual void SerializeAttribute(const String & className, const String & elementName, const String & attributeName, int var);
        virtual void SerializeAttribute(const String & className, const String & elementName, const String & attributeName, const String & var);
        
        virtual void DeserializeElement(const String & className, const String & elementName, bool &var);
        virtual void DeserializeElement(const String & className, const String & elementName, int & var);
        virtual void DeserializeElement(const String & className, const String & elementName, String & var);
        virtual void DeserializeElement(const String & className, const String & elementName, IXmlSerializable & var);

        virtual void DeserializeAttribute(const String & className, const String & elementName, const String & attributeName, bool & var);
        virtual void DeserializeAttribute(const String & className, const String & elementName, const String & attributeName, int & var);
        virtual void DeserializeAttribute(const String & className, const String & elementName, const String & attributeName, String & var);
   };

#define BEGIN_XML_SERIALIZER_BASE(className, baseClassName)   \
    virtual void Serialize(XmlSerializer * serializer, bool serialize) \
    {                                                                  \
        const String __className = TEXT(#className);                    \
                                                                       \
        if (IsTypeDerivedFrom<baseClassName, IXmlSerializable>::Yes)      \
        {                                                               \
            baseClassName::Serialize(serializer, serialize);            \
        }

#define BEGIN_XML_SERIALIZER(className) \
    virtual void Serialize(XmlSerializer * serializer, bool serialize) \
    {                                                                  \
        const String __className = TEXT(#className);                    

#define END_XML_SERIALIZER()    \
    }

#define XML_ELEMENT(var)        \
        if (serialize)          \
        {                       \
            serializer->SerializeElement(__className, TEXT(#var), var); \
        }   \
        else \
        {   \
            serializer->DeserializeElement(__className, TEXT(#var), var);   \
        }

#define XML_ATTRIBUTE(element, var)      \
        if (serialize)          \
        {                       \
            serializer->SerializeAttribute(__className, TEXT(#element), TEXT(#var), var); \
        }   \
        else \
        { \
            serializer->DeserializeAttribute(__className, TEXT(#element), TEXT(#var), var); \
        }

 

有一个模板类 IsTypeDerivedFrom<T, U>用于判断类型T是否从类型U派生。如果是,那么Yes=1,否则Yes=0。

为了序列化一个类,首先从 IXmlSerializable 接口派生(实际上如果这个类不作为其它序列化类的基类的化,也可以不用从该接口派生),然后使用 BEGIN_XML_SERIALIZER (对于单独的类)或者 BEGIN_XML_SERIALIZER_BASE(对于需要序列化基类的情况)开始定义序列化器。例如:

 

class ClassA: public IXmlSerializable
{
public:
    BEGIN_XML_SERIALIZER(ClassA)
        XML_ELEMENT(m_data)
        XML_ATTRIBUTE(m_data, m_value)
        XML_ELEMENT(m_bool)
        XML_ELEMENT(m_s)
    END_XML_SERIALIZER();

    ClassA()
        : m_data(0),
          m_value(1),
          m_bool(false),
          m_s(TEXT("Hello"))
    {
    }

    void Reset()
    {
        m_data = -1;
        m_value = 0;
        m_bool = true;
        m_s = TEXT("");
    }

private:
    int m_data;
    int m_value;
    bool m_bool;
    String m_s;
};

class ClassC: public ClassA
{
public:
    BEGIN_XML_SERIALIZER_BASE(ClassC, ClassA)
        XML_ELEMENT(m_data)
        XML_ATTRIBUTE(m_data, m_value)
        XML_ELEMENT(m_bool)
        XML_ELEMENT(m_s)
        XML_ELEMENT(m_a)
    END_XML_SERIALIZER();

    ClassC()
        : m_data(0),
          m_value(1),
          m_bool(false),
          m_s(TEXT("Hello"))
    {
    }

    void Reset()
    {
        m_data = -1;
        m_value = 0;
        m_bool = true;
        m_s = TEXT("");
    }

private:
    int m_data;
    int m_value;
    bool m_bool;
    String m_s;
    ClassA m_a;
};

 

执行下面的代码:

 

XmlSerializer serializer;
MemoryStream ms;

ms.Open();
serializer->OpenSerializer(&ms);

ClassC c;

c.Serialize(&serializer, true);

serializer.Close();

const char * buf = static_cast<const char *>(ms.GetBuffer());

 

这样 buf 中就包含了序列化以后的XML文本,如下:

<SerializeData>
 <ClassA>
  <m_bool>false</m_bool>
  <m_data m_value="1">0</m_data>
  <m_s>Hello</m_s>
 </ClassA>
 <ClassC>
  <m_a>
   <ClassA>
    <m_bool>false</m_bool>
    <m_data m_value="1">0</m_data>
    <m_s>Hello</m_s>
   </ClassA>
  </m_a>
  <m_bool>false</m_bool>
  <m_data m_value="1">0</m_data>
  <m_s>Hello</m_s>
 </ClassC>
</SerializeData>


实际的序列化后的文本是不包含缩进的,上面这样只是为了便于浏览。

使用 c.Serialize(&serializer, false) 可以执行反序列化,即从 XML 文本生成对应的对象。

【限制】

目前尚没有实现对数组的序列化,对于指针,考虑不做序列化,原因在于涉及到内存的分配以及动态多态等问题不是很好解决。

posted @ 2007-11-21 16:06 Ling Xu 阅读(5308) | 评论 (4)编辑 收藏