1. 回调的定义
回调或回调函数是对作为参数传递给另一端代码的可执行代码的任何引用,该代码应作为其工作的一部分执行回调函数。这个执行可能会像在同步回调中那样立即执行,也可能像在异步回调中那样在稍晚的时间点发生。编程语言以不同的方式支持回调,通常使用子例程、lambda表达式、块或者函数指针。
2. 使用回调函数的优点
回调函数能够降低耦合、提高代码的可重用性,同时方便我们扩展功能。
lab5.2中回调函数的使用使得查找具体命令的实现不在需要在linkTable.c中给出具体实现,降低了不同文件之间的耦合、提高的有关代码的可重用性;
menu.c需要实现其他功能时也可以通过修改回调函数的方式拓展功能,而不再需要去修改linkTable.c,方便了功能的扩展。
3. lab5.2 代码分析
首先我们在linktableInternal.h头文件中定义了LinkTableNode和LinkTable,其中LinkTableNode仅定义了指向下一个节点的指针,LinkTable指定了头尾节点、链表中节点数量和用于互斥操作的锁。具体代码如下:
/*
* LinkTable Node Type
*/
struct LinkTableNode
{
struct LinkTableNode * pNext;
};
/*
* LinkTable Type
*/
struct LinkTable
{
struct LinkTableNode *pHead;
struct LinkTableNode *pTail;
int SumOfNode;
pthread_mutex_t mutex;
};
接下来我们在linktable.h中将LinkTableNode和LinkTable重新定义为了tLinkTableNode和tLinkTable,这样可以降低linktableInternal.h中节点或链表结构更改对linktable.h的影响。随后我们又定义了对tLinkTableNode和tLinkTable的一系列操作,包括:创建链表、删除链表、插入节点、删除节点、查找结点、获取链表头和获取下一个节点。具体实现则是在linktable.c中实现的,这样一来可以对上层用户隐藏实现的细节,减少上层用户有意或者无意的破坏。
随后我们在主函数中,定义了我们用来存储菜单命令的数据结构tDataNode,代码如下:
typedef struct DataNode
{
tLinkTableNode head;
char* cmd;
char* desc;
int (*handler)();
} tDataNode;
接下来就是利用回调函数实现菜单命令的具体部分。
为了实现回调函数,我们首先定义了一个call in方式的函数SearchLinkTableNode,其定义如下:
/*
* Search a LinkTableNode from LinkTable
* int Conditon(tLinkTableNode * pNode, void * args);
*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode, void * args), void * args);
SearchLinkTableNode内部调用的函数Condition就是回调函数。其具体实现可见menu.c中的SearchCondition函数,其实现如下:
int SearchCondition(tLinkTableNode * pLinkTableNode, void * args)
{
char * cmd = (char*) args;
tDataNode * pNode = (tDataNode *)pLinkTableNode;
if(strcmp(pNode->cmd, cmd) == 0)
{
return SUCCESS;
}
return FAILURE;
}
主函数中调用SearchLinkTableNode函数查找节点,但具体的操作细节却留给传递过去的SearchCondition函数。SearchCondition函数通过比较每个节点中的cmd来实现查找菜单命令。
这里有两个点值得我们接着展开:
Q1: SearchLinkTableNode函数中的args参数和SearchLinkTableNode函数中的args参数
Q2: tLinkTableNode强制转换成tDataNode
Q1: SearchLinkTableNode函数中的args参数和SearchLinkTableNode函数中的args参数
主函数中调用FindCmd函数时,传给SearchLinkTableNode的是(void*)cmd
/* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
return (tDataNode*)SearchLinkTableNode(head, SearchCondition, (void*)cmd);
}
而在SearchLinkTableNode将args传递给SearchCondition后,SearchCondition又将args还原成了cmd:
int SearchCondition(tLinkTableNode * pLinkTableNode, void * args)
{
char * cmd = (char*) args;
tDataNode * pNode = (tDataNode *)pLinkTableNode;//这样的强制类型转换在工程文件中是否提倡使用呢
if(strcmp(pNode->cmd, cmd) == 0)
{
return SUCCESS;
}
return FAILURE;
}
这样一来,进一步降低了不同代码块之间的耦合。
Q2: tLinkTableNode强制转换成tDataNode
tLinkTableNode中只有指向下一个节点的指针,而tDataNode中不仅定义了指向下一个节点的指针还定义了其他和菜单功能有关的数据结构。通过强制将tLinkTableNode转换成tDataNode完成了基础数据结构和用户所需数据结构之间的转换。这是C语言中多态的一种形式,通过父类和子类指针之间的转换,实现对不同对象之间的操作。
学号后三位:346