阅读开源项目FBReader 程序源码的时候,发现ZLXMLReader在解析XML时,采用了一种特殊的机制。解析时,进入XML
TAG和退出XML TAG时,C++类ZLXMLReader的startElementHandler 和 endElementHandler
类成员函数被eXpat (C语言实现的XML解析库) 函数不停的调用。我仔细查看了一下,并仿写了一个类似的机制。
//CB.h
- #ifndef _CB_H_
- #define _CB_H_
-
- #include "CStyle.h"
-
- class CallBackUserData;
-
- class CallBackInternal
- {
- public:
- CallBackInternal(CallBackUserData &a_C_UserData);
- virtual ~CallBackInternal();
-
- void Do();
-
- private:
- static void StartSthFunc( void *a_pv_UserData, const char *a_psz_Name );
- static void EndSthFunc( void *a_pv_UserData, const char *a_psz_Name );
-
- private:
- CStyleStruct_S * m_S_Parser;
- CallBackUserData &m_C_UserData;
- };
-
- class CallBackUserData
- {
- public:
- CallBackUserData();
- ~CallBackUserData();
- public:
- void StartSth( const char * a_psz_Name );
- void EndSth( const char * a_psz_Name );
- void Do();
- private:
- CallBackInternal *m_C_Internal;
- };
-
- #endif
//CB.cpp
- #include
- #include "CB.h"
-
- CallBackInternal::CallBackInternal(CallBackUserData &a_C_UserData) : m_C_UserData(a_C_UserData)
- {
- m_S_Parser = CreateParser();
- SetUserData(m_S_Parser, &m_C_UserData );
- SetStartHandler(m_S_Parser, StartSthFunc);
- SetEndHandler(m_S_Parser, EndSthFunc );
- }
-
- CallBackInternal::~CallBackInternal()
- {
- FreeParser(m_S_Parser);
- }
-
- void CallBackInternal::StartSthFunc( void *a_pv_UserData, const char *a_psz_Name )
- {
- CallBackUserData &reader = *(CallBackUserData*)a_pv_UserData;
- reader.StartSth(a_psz_Name);
-
-
- }
-
- void CallBackInternal::EndSthFunc( void *a_pv_UserData, const char *a_psz_Name )
- {
- CallBackUserData &reader = *(CallBackUserData*)a_pv_UserData;
- reader.EndSth(a_psz_Name);
- }
-
- void CallBackInternal::Do()
- {
- DoSth(m_S_Parser);
- }
-
-
-
-
- CallBackUserData::CallBackUserData()
- {
- m_C_Internal = new CallBackInternal(*this);
- }
-
- CallBackUserData::~CallBackUserData()
- {
- if (0==m_C_Internal)
- {
- delete m_C_Internal;
- m_C_Internal = 0;
- }
- }
-
- void CallBackUserData::StartSth( const char * a_psz_Name )
- {
- printf("CallBackUserData::StartSth()\n");
- printf("C库中解析出来的数据作为参数: %s\n",a_psz_Name);
- }
-
- void CallBackUserData::EndSth( const char * a_psz_Name )
- {
- printf("CallBackUserData::EndSth()\n");
- printf("C库中解析出来的数据作为参数: %s\n",a_psz_Name);
- }
-
- void CallBackUserData::Do()
- {
- m_C_Internal->Do();
- }
//CStyle.h
- #ifndef _C_STYLE_H_
- #define _C_STYLE_H_
-
- #include
- #include
-
- #ifdef __cplusplus
- extern "C"
- {
- #endif
-
- typedef void (*StartHandler)( void *a_pv_UserData, const char *a_psz_Name );
- typedef void (*EndHandler)( void *a_pv_UserData, const char *a_psz_Name );
-
- typedef struct _CStyleStruct_S
- {
- void *m_pv_UserData;
- StartHandler m_p_StartHandler;
- EndHandler m_p_EndHandler;
- }CStyleStruct_S;
-
- CStyleStruct_S *CreateParser();
- void SetUserData(CStyleStruct_S* a_S_This, void *a_pv_UserData);
- void SetStartHandler(CStyleStruct_S* a_S_This, void *a_pv_FuncPtr);
- void SetEndHandler(CStyleStruct_S* a_S_This, void *a_pv_FuncPtr);
- void DoSth(CStyleStruct_S* a_S_This);
- void FreeParser(CStyleStruct_S *a_S_This);
-
- #ifdef __cplusplus
- }
- #endif
-
- #endif
//CStyle.c
- #include "CStyle.h"
-
- #define userData (a_S_This->m_pv_UserData)
- #define startHanler (a_S_This->m_p_StartHandler)
- #define endHanler (a_S_This->m_p_EndHandler)
-
- CStyleStruct_S *CreateParser()
- {
- CStyleStruct_S *t_S_p = (CStyleStruct_S *)malloc(sizeof(CStyleStruct_S));
-
-
-
-
- return t_S_p;
- }
-
- void SetUserData(CStyleStruct_S* a_S_This, void *a_pv_UserData)
- {
- userData = a_pv_UserData;
- }
-
- void SetStartHandler(CStyleStruct_S* a_S_This, void *a_pv_FuncPtr)
- {
- startHanler = a_pv_FuncPtr;
- }
-
- void SetEndHandler(CStyleStruct_S* a_S_This, void *a_pv_FuncPtr)
- {
- endHanler = a_pv_FuncPtr;
- }
-
- void DoSth(CStyleStruct_S* a_S_This)
- {
- startHanler(userData, "start_");
- endHanler(userData, "end_");
- }
-
- void FreeParser(CStyleStruct_S *a_S_This)
- {
- if (a_S_This)
- {
- free(a_S_This);
- a_S_This = 0;
- }
- }
main函数中这样调用:
- #include
- #include
- #include "CB.h"
-
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- CallBackUserData reader;
- reader.Do();
- system("pause");
- return 0;
- }
调用顺序:
1. 当调用 main函数中的 CallBackUserData reader; reader.Do();
2. reader.Do(); 调用了CallBackInternal->Do();
3. 中介者CallBackInternal 的Do函数,又调用了 C语言的 函数 DoSth(m_S_Parser);
4. DoSth 又以函数指针的形式,调用了 CallBackInternal 类的静态成员函数StartSthFunc和EndSthFunc,并以参数形式传递在C库中解析出的内容到这两个静态函数。
5. 在这两个静态函数中,又调用了CallBackUserData 的 StartSth和EndSth。
说明:
1. 类CallBackUserData是一个用户自定义的数据结构,这里以类的形式描述。
2. 类CallBackInternal 则是C与C++相互调用的中间者,起到了一个中介的作用。
3. CStyle.h 和 CStyle.cpp中的类则是C风格的方式。
这样一个机制,通过函数指针与静态类成员函数,实现了C与C++的交互,在C中调用C++的类成员函数,即相当于在C库的DoSth中,调用了
CallBackUserData 中的StartSth 和 EndSth, C库解析的内容,则作为 StartSth 和 EndSth 的参数。
CallBackUserData 的函数 Do(); 相当于绕了一个弯子,进入了C函数库,与直接调用CallBackUserData::StartSth() 与CallBackUserData::EndSth()的结果是一样的。
实际应用:
eXpat 和 FBReader中的ZLXMLReader 类的这种机制,使得eXpat库解析到一个 的XML标签时,进入解析时,调用StartSth (startElementHandler),
退出解析时,调用EndSth (endElementHandler), 而且 BTN, id=100 均与 参数形式被传出到 StartSth,
EndSth 中, 使用者就可以知道是解析到了什么。
我们将CallBackUserData (ZLXMLReader ) 作为基类, 并派生相应的类。并在派生类中重载 StartSth
(startElementHandler), EndSth (endElementHandler), 就可以利用XML配置派生类相应的属性。
使用这种机制,就将eXpat库进行了包裹,方便了使用,是一种很灵巧的方法。
运行结果:
呃。。。,有点不知所云。(表达能力欠缺啊)
阅读(2139) | 评论(0) | 转发(1) |