> 文章列表 > Linktable增加Callback方式的接口

Linktable增加Callback方式的接口

Linktable增加Callback方式的接口

首先我们要明确什么是回调函数,以及回调函数什么时候发生作用。

回调函数是通过函数指针调用的函数。把函数的指针(地址)作为参数传递给另一个函数,当这个指针调用其所指向的函数时,就称这是回调函数。回调函数不是实现该函数的软件模块直接调用,而是在特定的事件或条件发生时由另外的软件模块通过函数指针的方式调用,用于对该事件或条件进行响应,是一种下层软件模块调用上层软件模块的特殊方式。回调函数应用场景的简要示意图如下所示。

给Linktable增加Callback方式的接口,需要两个函数接口,一个是call-in方式函数,如SearchLinkTableNode函数,其中有一个函数作为参数,这个作为参数的函数就是callback函数,即代码中Conditon函数。由于我们在工程化C编程中追求松散耦合,所以给call-in方式的函数接口SearchLinkTableNode增加了一个参数args,callback函数Conditon也增加了一个参数args。args用于传递用户输入的菜单命令(help、version和quit)。

/** Serach a LinkTableNode from LinkTable* int Condition(tLinkTableNode *pNode, void * args);*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Condition(tLinkTableNode *pNode, void *args), void * args);

该接口对应的实现代码如下:

/** Serach a LinkTableNode from LinkTable* int Condition(tLinkTableNode *pNode, void * args);*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Condition(tLinkTableNode *pNode, void *args), void * args)
{if (pLinkTable == NULL || Condition == NULL){return NULL;}tLinkTableNode * pNode = pLinkTable->pHead;while (pNode != NULL){if (Condition(pNode, args) == SUCCESS){return pNode;}pNode = pNode->pNext;}return NULL;
}

显然该实现代码中没有实现Condition函数,而Condition是用户代码传进来的一个函数指针,只是我们在回调函数接口定义时规定了传进来的函数指针的规格。

我们再来看menu菜单项目中采用Callback方式的函数接口来查询链表的应用示例代码。

typedef struct DataNode
{tLinkTableNode head;char* cmd;char* desc;int (*handler)();
}tDataNode;int SearchCondition(tLinkTableNode *pLinkTableNode, void *args)
{char * cmd = (char*) args;tDataNode * pNode = (tDataNode *)pLinkTableNode;if (strcmp(pNode->cmd, cmd) == 0){return SUCCESS;}return FAILURE;
}/* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{return (tDataNode *)SearchLinkTableNode(head, SearchCondition, (void *)cmd);
}

 main函数中调用FindCmd函数如下:

int main()
{InitMenuData(&head);/* cmd line begins */while (1){char cmd[CMD_MAX_LEN];printf("Input cmd > ");scanf("%s", cmd);tDataNode *p = FindCmd(head, cmd);if (p == NULL){printf("This is a wrong cmd!\\n");continue;}printf("%s - %s\\n", p->cmd, p->desc); if (p->handler != NULL){p->handler();}}return 0;
}

具体到这个menu项目,回调函数工作流程大致如下图所示。 

由于在具体业务模块,我们已经明确传入的args参数为用户输入的指令(如help、version或quit),所以我们可以将void *args强制类型转换为char *类型,然后根据char *类型进行后续的处理。

但是在通用软件模块,我们为了保证此模块的通用性,需要将传入的参数args定义为void *类型,以便于今后其他业务模块复用此模块。

这种用法让笔者想到设计模式中的模板方法模式,在模板方法模式中,子类继承父类的模板方法(该方法由final修饰),然后通过在子类中实现钩子函数,进而控制父类中模板方法的执行过程,详情可查看笔者的这篇博客行为型模式-模板方法模式_青衫客36的博客-CSDN博客,具体的实现类通过实现自己的isPrint()函数(即钩子函数),进而控制cookProcess()函数的输出结果。

在menu菜单项目中,还有一个非常精彩的设计值得参考,那就是具体业务模块封装的DataNode结构体,具体声明如下:

typedef struct DataNode
{tLinkTableNode head;char* cmd;char* desc;int (*handler)();
}tDataNode;

其中tLinkTableNode的结构体如下所示,仅包含一个指向下一个节点的指针。

struct LinkTableNode
{struct LinkTableNode * pNext;
};

 

由上图我们可以发现,在业务模块初始化Menu菜单时,我们使用malloc开辟了tDataNode类型的空间,然后把其中的成员变量赋值,注意最后我们在向LinkTable链表中插入时,对其进行了强制类型转换,转换成了tLinktableNode类型,因为LinkTable中的节点tLinktableNode中,成员只有一个(即指向自己后继的指针),那这样强制类型转换后,tDataNode中的其他成员的属性值是不是就丢失了呢?答案是否定的,我们来看一张图可能就明白了。

LinkTable链表中的节点是与业务无关的,仅仅是存放LinkTableNode而已,所以当我们对LinkTable链表执行增加节点操作时,要进行强制类型转换,由于业务中的结构体DataNode中的第一个成员就是LinkTableNode,所以二者在内存中的起始地址相同,故可以类型转换后插入。上图中的红色部分并没有消失,等我们取出LinkTable中的节点时,再强制类型转换为DataNode类型就可以再次看到Data部分的数据了。

由函数返回类型可以看出,在LinkTable中查找节点返回的是LinkTableNode*,在业务层我们对其进行强制类型转换,转换为DataNode类型,就可以看到该节点剩余部分的数据了。

程序运行结果如下图所示

 

menu项目完整源代码如下:

linktableInternal.h文件

#include <pthread.h>/** 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文件

#ifndef _LINK_TABLE_H_
#define _LINK_TABLE_H_#include "linktableInternal.h"#define SUCCESS 0
#define FAILURE (-1)/** LinkTable Node Type*/
typedef struct LinkTableNode tLinkTableNode;/** LinkTable Type*/
typedef struct LinkTable tLinkTable;/** Create a LinkTable*/
tLinkTable * CreateLinkTable();/** Delete a LinkTable*/
int DeleteLinkTable(tLinkTable *pLinkTable);/** Add a LinkTableNode to LinkTable*/
int AddLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode *pNode);/** Delete a LinkTableNode from LinkTable*/
int DelLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode *pNode);/** Serach a LinkTableNode from LinkTable* int Condition(tLinkTableNode *pNode, void * args);*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Condition(tLinkTableNode *pNode, void *args), void * args);/** get LinkTableHead*/
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);/** get next LinkTableNode*/
tLinkTableNode *GetNextLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode *pNode);#endif /* _LINK_TABLE_H_ */

 linktable.c文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "linktable.h"/** Create a LinkTable*/
tLinkTable * CreateLinkTable()
{tLinkTable * pLinkTable = (tLinkTable *)malloc(sizeof(tLinkTable));if (pLinkTable == NULL){return NULL;}pLinkTable->pHead = NULL;pLinkTable->pTail = NULL;pLinkTable->SumOfNode = 0;pthread_mutex_init(&(pLinkTable->mutex), NULL);return pLinkTable;
}/** Delete a LinkTable*/
int DeleteLinkTable(tLinkTable *pLinkTable)
{if (pLinkTable == NULL){return FAILURE;}while (pLinkTable->pHead != NULL){tLinkTableNode *p = pLinkTable->pHead;pthread_mutex_lock(&(pLinkTable->mutex));pLinkTable->pHead = pLinkTable->pHead->pNext;pLinkTable->SumOfNode -= 1;pthread_mutex_unlock(&(pLinkTable->mutex));free(p);}pLinkTable->pHead = NULL;pLinkTable->pTail = NULL;pLinkTable->SumOfNode = 0;pthread_mutex_destroy(&(pLinkTable->mutex));free(pLinkTable);return SUCCESS;
}/** Add a LinkTableNode to LinkTable*/
int AddLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode *pNode)
{if (pLinkTable == NULL || pNode == NULL){return FAILURE;}pNode->pNext = NULL;pthread_mutex_lock(&(pLinkTable->mutex));if (pLinkTable->pHead == NULL){pLinkTable->pHead = pNode;}if (pLinkTable->pTail == NULL){pLinkTable->pTail = pNode;}else{pLinkTable->pTail->pNext = pNode;pLinkTable->pTail = pNode;}pLinkTable->SumOfNode += 1;pthread_mutex_unlock(&(pLinkTable->mutex));return SUCCESS;
}/** Delete a LinkTableNode from LinkTable*/
int DelLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode *pNode)
{if (pLinkTable == NULL || pNode == NULL){return FAILURE;}pthread_mutex_lock(&(pLinkTable->mutex));if (pLinkTable->pHead == pNode){pLinkTable->pHead = pLinkTable->pHead->pNext;pLinkTable->SumOfNode -= 1;if (pLinkTable->SumOfNode == 0){pLinkTable->pTail = NULL;}pthread_mutex_unlock(&(pLinkTable->mutex));return SUCCESS;}tLinkTableNode * pTempNode = pLinkTable->pHead;while (pTempNode != NULL){if (pTempNode->pNext == pNode){pTempNode->pNext = pTempNode->pNext->pNext;pLinkTable->SumOfNode -= 1;if (pLinkTable->SumOfNode == 0){pLinkTable->pTail = NULL;}pthread_mutex_unlock(&(pLinkTable->mutex));return SUCCESS;}pTempNode = pTempNode->pNext;}pthread_mutex_unlock(&(pLinkTable->mutex));return FAILURE;
}/** Serach a LinkTableNode from LinkTable* int Condition(tLinkTableNode *pNode, void * args);*/
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Condition(tLinkTableNode *pNode, void *args), void * args)
{if (pLinkTable == NULL || Condition == NULL){return NULL;}tLinkTableNode * pNode = pLinkTable->pHead;while (pNode != NULL){if (Condition(pNode, args) == SUCCESS){return pNode;}pNode = pNode->pNext;}return NULL;
}/** get LinkTableHead*/
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable)
{if (pLinkTable == NULL){return NULL;}return pLinkTable->pHead;
}/** get next LinkTableNode*/
tLinkTableNode *GetNextLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode *pNode)
{if (pLinkTable == NULL || pNode == NULL){return NULL;}tLinkTableNode * pTempNode = pLinkTable->pHead;while (pTempNode != NULL){if (pTempNode == pNode){return pTempNode->pNext;}pTempNode = pTempNode->pNext;}return NULL;
}

menu.c文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linktable.h"int Help();
int Quit();#define CMD_MAX_LEN 128
#define DESC_LEN 1024
#define CMD_NUM 10typedef struct DataNode
{tLinkTableNode head;char* cmd;char* desc;int (*handler)();
}tDataNode;int SearchCondition(tLinkTableNode *pLinkTableNode, void *args)
{char * cmd = (char*) args;tDataNode * pNode = (tDataNode *)pLinkTableNode;if (strcmp(pNode->cmd, cmd) == 0){return SUCCESS;}return FAILURE;
}/* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{return (tDataNode *)SearchLinkTableNode(head, SearchCondition, (void *)cmd);
}/* show all cmd in listlist */
int ShowAllCmd(tLinkTable * head)
{tDataNode * pNode = (tDataNode *)GetLinkTableHead(head);while (pNode != NULL){printf("%s - %s\\n", pNode->cmd, pNode->desc);pNode = (tDataNode *)GetNextLinkTableNode(head, (tLinkTableNode *)pNode);}return 0;
}int InitMenuData(tLinkTable ** ppLinktable)
{*ppLinktable = CreateLinkTable();tDataNode* pNode = (tDataNode *)malloc(sizeof(tDataNode));pNode->cmd = "help";pNode->desc = "Menu List:";pNode->handler = Help;AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);pNode = (tDataNode *)malloc(sizeof(tDataNode));pNode->cmd = "version";pNode->desc = "Menu Program V1.0";pNode->handler = NULL;AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);pNode = (tDataNode *)malloc(sizeof(tDataNode));pNode->cmd = "quit";pNode->desc = "Quit from Menu Program V1.0";pNode->handler = Quit;AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);return 0;
}/* menu program */tLinkTable * head = NULL;int main()
{InitMenuData(&head);/* cmd line begins */while (1){char cmd[CMD_MAX_LEN];printf("Input cmd > ");scanf("%s", cmd);tDataNode *p = FindCmd(head, cmd);if (p == NULL){printf("This is a wrong cmd!\\n");continue;}printf("%s - %s\\n", p->cmd, p->desc); if (p->handler != NULL){p->handler();}}return 0;
}int Help()
{printf("This is help can do for you:\\n");ShowAllCmd(head);return 0;
}int Quit()
{printf("Bye Bye\\n");exit(0);
}

西河女性网