阅读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