阅读citadel的代码,后来吧,有时候就是比较喜欢追新。就将citadel的最新版本给下下来了。也就是其git上的版本。还是有不少的收获。其XMPP模块也进行了重构,从代码上看,更加容易进行扩展等一系列的操作了。
首先,当然是先把代码都给clone下来。(我此次git的版本为commit 6050cb23108ee10dafcceb65b9cafab51c013ae0)
git clone git://git.citadel.org/appl/gitroot/citadel.git
这是把citadel上的所有工程代码都给clone下来。
cd citadel ll 总用量 48 drwxrwxr-x 2 ping ping 4096 6月 5 12:34 buildbot drwxrwxr-x 19 ping ping 12288 6月 12 16:35 citadel drwxrwxr-x 3 ping ping 4096 6月 5 12:34 contrib drwxrwxr-x 4 ping ping 4096 6月 5 12:34 ctdlphp drwxrwxr-x 3 ping ping 4096 6月 5 12:34 ctdlsalearn drwxrwxr-x 3 ping ping 4096 6月 5 12:34 ctdlsh drwxrwxr-x 9 ping ping 4096 6月 9 10:22 libcitadel -rwxrwxr-x 1 ping ping 4016 6月 5 12:34 releaseversion.sh drwxrwxr-x 6 ping ping 4096 6月 5 12:34 textclient drwxrwxr-x 11 ping ping 4096 6月 9 18:54 webcit
内容还不少的。其中服务端的话,是citadel那个目录。大概看了下,XMPP模块的话,已经被细分成多个模块了。下文将以XMPP模块为主,进行说明。
新的xmpp模块由原来的单个xmpp模块划分成了xmpp+xmpp_message+xmpp_presence+xmpp_queue+xmpp_xmacros。其中比较重要的就是xmpp_xmacros,这个模块,算是做了一个宏模板。也因为添加了该模块,出现了一个比较奇葩的定义——token.def,这个说是头文件吧,又不像,但是却又当头文件来引用。说起这个,我以前一个同事,在引用头文件的时候,引用了某个.c当做头文件,并且能够编译也可以执行。真是把一直用.h做头文件的习惯给打破了。这是后话了。token.def文件的内容如下:
#define NAMESPACE_iq "jabber:client" TOKEN(iq, { STRPROP(iq, type); STRPROP(iq, id); STRPROP(iq, from); STRPROP(iq, to); }) #define NAMESPACE_piq "bub" TOKEN(piq, { STRPROP(piq, type); STRPROP(piq, id); STRPROP(piq, from); STRPROP(piq, to); }) #define NAMESPACE_message "jabber:client" TOKEN(message, { STRPROP(message, to); STRPROP(message, type); STRPROP(message, id); PAYLOAD(message, body); }) // <message type='chat' id='purplef5a7ed34' to='testuser@blarg.potzblitz.outgesourced.org'><active xmlns='http://jabber.org/protocol/chatstates'/><body>gci</body></message> // <iq type="result" id="unsolicited_2"><query xmlns="jabber:iq:roster"><item subscription="both" jid="willi@potzblitz.outgesourced.org" name="testuser"><group>potzblitz</group></item></query></iq><presence>testuser@blarg.potzblitz.outgesourced.org to="willi@potzblitz.outgesourced.org/potzblitz"</presence><message type="chat" to="willi@potzblitz.outgesourced.org/potzblitz" from="testuser@blarg.potzblitz.outgesourced.org"><body>rgilgci</body></message>
再看看其被引用的地方:
ping@GE60-Kubuntu ~/source/citadel-git/citadel (git)-[master] % grep -rn "token.def" * modules/xmpp/xmpp_xmacros.c:60:#include "token.def" modules/xmpp/xmpp_xmacros.c:78:#include "token.def" modules/xmpp/xmpp_xmacros.c:90:#include "token.def" modules/xmpp/xmpp_xmacros.c:120:#include "token.def" modules/xmpp/xmpp_xmacros.h:11:#include "token.def" modules/xmpp/xmpp_xmacros.h:24:#include "token.def" modules/xmpp/xmpp_xmacros.h:36:#include "token.def"
从.c开始看吧,也就是从xmpp_xmacros.c开始吧,该文件代码并不多,把与token.def相关的贴出来:
#define STRPROP(STRUCTNAME, NAME) \ if (StrLength(pdata->NAME) > 0) \ { \ XPut(#NAME, sizeof(#NAME) - 1); \ XPut("=\"", 2); \ XPutProp(SKEY(pdata->NAME)); \ XPut("\" ", 2); \ } #define PAYLOAD(STRUCTNAME, NAME) \ XPrint(#NAME, sizeof(#NAME) -1, \ XCLOSED, \ TYPE_BODYSTR, SKEY(pdata->NAME), \ TYPE_ARGEND); #define THENAMESPACE(STRUCTNAME, NAME) \ XPut(#NAME, sizeof(#NAME) - 1); \ XPut("=\"", 2); \ XPutProp(NAMESPACE_##STRUCTNAME, \ sizeof(NAMESPACE_##STRUCTNAME)-1); \ XPut("\" ", 2); #define TOKEN(NAME, STRUCT) \ void serialize_##NAME(TheToken_##NAME *pdata, int Close) \ { \ XPUT("<"); \ XPut(#NAME, sizeof(#NAME)); \ XPUT(" "); \ STRUCT ; \ XPUT(">"); \ if (Close) \ { \ XPut("</", 2); \ XPut(#NAME, sizeof(#NAME)); \ XPut(">", 1); \ } \ } #include "token.def" #undef STRPROP #undef PAYLOAD #undef TOKEN #define STRPROP(STRUCTNAME, NAME) \ FreeStrBuf(&pdata->NAME); #define PAYLOAD(STRUCTNAME, NAME) \ FreeStrBuf(&pdata->NAME); #define TOKEN(NAME, STRUCT) \ void free_buf_##NAME(TheToken_##NAME *pdata) \ { \ STRUCT ; \ } #include "token.def" #undef STRPROP #undef PAYLOAD #undef TOKEN #define TOKEN(NAME, STRUCT) \ void free_##NAME(TheToken_##NAME *pdata) \ { \ free_buf_##NAME(pdata); \ free(pdata); \ } #include "token.def" #undef STRPROP #undef TOKEN
说白了,就是通过每次对STRPROP,PAYLOAD及TOKEN这三个宏定义进行重构,就定义出不同的函数出来。很巧妙的利用宏定义。也正是这样,所以就起了个很奇葩的后缀.def,以IQ这个定义来展开,首次展开的内容应该是这样的:
/* 首次定义TOKEN是生成serialize_iq这个函数 */ void serialize_iq(TheToken_iq *pdata, int Close) /* ## 表示前后两个单词拼接在一起,##NAME展开 */ { XPUT("<"); XPut("iq", sizeof("iq")); /* # 则是变成字符串了,#NAME展开 */ XPUT(" "); /* STRUCT ; 这个的话,在iq这个定义里面对应了: { STRPROP(message, to); STRPROP(message, type); STRPROP(message, id); PAYLOAD(message, body); } ,展开略为复杂 */ { /* 从这里开始展开 */ if (StrLength(pdata->type) > 0) /* NAME展开为type */ { XPut("type", sizeof("type") - 1); XPut("=\"", 2); XPutProp(SKEY(pdata->type)); XPut("\" ", 2); } if (StrLength(pdata->id) > 0) /* NAME展开为id */ { XPut("id", sizeof("id") - 1); XPut("=\"", 2); XPutProp(SKEY(pdata->id)); XPut("\" ", 2); } if (StrLength(pdata->from) > 0) /* NAME展开为from */ { XPut("type", sizeof("from") - 1); XPut("=\"", 2); XPutProp(SKEY(pdata->from)); XPut("\" ", 2); } if (StrLength(pdata->to) > 0) /* NAME展开为to */ { XPut("to", sizeof("to") - 1); XPut("=\"", 2); XPutProp(SKEY(pdata->to)); XPut("\" ", 2); } } /* 展开到这里结束 */ XPUT(">"); if (Close) { XPut("</", 2); XPut("iq", sizeof("iq")); /* 这里有 #NAME 展开 */ XPut(">", 1); } }
之后调用了#undef,将所有之前的宏定义都消除了。再次重新定义,重新定义的,则是生成了free_buf系列函数:
void free_buf_iq(TheToken_iq *pdata) /* ##NAME 展开为 iq */ { /* STRUCT ; 展开为如下 */ {/* 从这里开始展开 */ FreeStrBuf(&pdata->type); /* STRPROP(iq, type); */ FreeStrBuf(&pdata->id); /* STRPROP(iq, id); */ FreeStrBuf(&pdata->from); /* STRPROP(iq, from); */ FreeStrBuf(&pdata->to); /* STRPROP(iq, to); */ } /* 到这里展开结束 */ }
最后还有一个,就是free_系列的函数了:
void free_iq(TheToken_iq *pdata) /* ##NAME 展开为 iq */ { free_buf_iq(pdata); free(pdata); }
还有就是在函数CTDL_MODULE_INIT(xmpp_xmacros)里面还展开一次:
char *ctdl_module_xmpp_xmacros_init (int threading) { if (!threading) { { long offsettype = offsetof(TheToken_iq, type); long offsetid = offsetof(TheToken_iq, id); long offsetfrom = offsetof(TheToken_iq, from); long offsetto = offsetof(TheToken_iq, to); XMPP_RegisterTokenProperty(NAMESPACE_iq, sizeof(NAMESPACE_iq), "iq", sizeof("iq")-1, "type", sizeof("type")-1, GetToken_iq, offsettype); XMPP_RegisterTokenProperty(NAMESPACE_iq, sizeof(NAMESPACE_iq), "iq", sizeof("iq")-1, "id", sizeof("id")-1, GetToken_iq, offsetid); XMPP_RegisterTokenProperty(NAMESPACE_iq, sizeof(NAMESPACE_iq), "iq", sizeof("iq")-1, "from", sizeof("from")-1, GetToken_iq, offsetfrom); XMPP_RegisterTokenProperty(NAMESPACE_iq, sizeof(NAMESPACE_iq), "iq", sizeof("iq")-1, "to", sizeof("to")-1, GetToken_iq, offsetto); } { long offsettype = offsetof(TheToken_piq, type); long offsetid = offsetof(TheToken_piq, id); long offsetfrom = offsetof(TheToken_piq, from); long offsetto = offsetof(TheToken_piq, to); XMPP_RegisterTokenProperty(NAMESPACE_piq, sizeof(NAMESPACE_piq), "piq", sizeof("piq")-1, "type", sizeof("type")-1, GetToken_piq, offsettype); XMPP_RegisterTokenProperty(NAMESPACE_piq, sizeof(NAMESPACE_piq), "piq", sizeof("piq")-1, "id", sizeof("id")-1, GetToken_piq, offsetid); XMPP_RegisterTokenProperty(NAMESPACE_piq, sizeof(NAMESPACE_piq), "piq", sizeof("piq")-1, "from", sizeof("from")-1, GetToken_piq, offsetfrom); XMPP_RegisterTokenProperty(NAMESPACE_piq, sizeof(NAMESPACE_piq), "piq", sizeof("piq")-1, "to", sizeof("to")-1, GetToken_piq, offsetto); } { long offsetto = offsetof(TheToken_piq, to); long offsettype = offsetof(TheToken_piq, type); long offsetid = offsetof(TheToken_piq, id); long offsetbody = offsetof(TheToken_piq, body); XMPP_RegisterTokenProperty(NAMESPACE_message, sizeof(NAMESPACE_message), "message", sizeof("message")-1, "to", sizeof("to")-1, GetToken_message, offsetto); XMPP_RegisterTokenProperty(NAMESPACE_message, sizeof(NAMESPACE_message), "message", sizeof("message")-1, "type", sizeof("type")-1, GetToken_message, offsettype); XMPP_RegisterTokenProperty(NAMESPACE_message, sizeof(NAMESPACE_message), "message", sizeof("message")-1, "id", sizeof("id")-1, GetToken_message, offsetid); XMPP_RegisterTokenProperty(NAMESPACE_message, sizeof(NAMESPACE_message), "message", sizeof("message")-1, NULL, 0, GetToken_piq, offsetto); } } return "xmpp_xmacros"; }
也就是说,在这个.c里面,通过宏的方式,把其定义的给展开成了一系列的函数及实现。而在头文件xmpp_xmacros.h中,测试变成了声明,还是以iq为例。
头文件内容如下:
/* * define the structures for one token each * typename: TheToken_<Tokenname> */ #define PAYLOAD(STRUCTNAME, NAME) StrBuf *NAME;int encoding_##NAME; #define STRPROP(STRUCTNAME, NAME) StrBuf *NAME; #define TOKEN(NAME, STRUCT) typedef struct __##NAME \ STRUCT \ TheToken_##NAME; #include "token.def" #undef STRPROP #undef PAYLOAD #undef TOKEN /* * forward declarations for freeing the members of one struct instance # name: free_buf_<Tokenname> */ #define TOKEN(NAME, STRUCT) \ void free_buf_##NAME(TheToken_##NAME *pdata); #include "token.def" #undef STRPROP #undef PAYLOAD #undef TOKEN /* * forward declarations, freeing structs and member. * name: free_<Tokenname> */ #define TOKEN(NAME, STRUCT) \ void free_##NAME(TheToken_##NAME *pdata); #include "token.def" #undef STRPROP #undef PAYLOAD #undef TOKEN
展开后对应为(竟然少了serialize_iq函数的定义。。。。好吧,可能是二妈生的):
typedef struct __iq { StrBuf *type; StrBuf *id; StrBuf *from; StrBuf *to; } TheToken_iq; void free_buf_iq(TheToken_iq *pdata); void free_iq(TheToken_iq *pdata);
从上面的工作量来看,和对应的简短的代码,就可以看出该宏定义的优点了,将重复的工作简单化,让编译器去瞎忙活就行了(累死我了,展开这些)。从现有重构的代码上来看,用到的其实就只有TheToken_iq及TheToken_message,那个TheToken_piq还没有看到有哪里有用到(和iq参数一样)。这样也就方便我们扩展了,因为XMPP其实是支持非常多的扩展的,总不能每次需要一个节的时候,你总要写一大票的代码,通过token.def,就可以扩展的时候,只是做这么一个工作:
#define NAMESPACE_iq "jabber:client" TOKEN(iq, { STRPROP(iq, type); STRPROP(iq, id); STRPROP(iq, from); STRPROP(iq, to); })
既快捷又方便。并不需要知道其再哪里有了什么样的实现。好了,taken.def就先说到这里了。XMPP模块还有很多的改动,如XPUT,原先的输出并不是使用该函数的。更多内容,下次的文章再进一步补充。
转载请注明: 转载自elkPi.com