回调函数是一种通过函数指针调用的函数,当该函数指针调用其所指向的函数时,就称这是回调函数。
回调函数通常不是由实现该函数的软件模块直接调用,而是在特定的事件或条件发生时由另外的软件模块通过函数指针的方式调用,用于对该事件或条件进行响应。
在之前写的menu程序的基础上,我们为Linktable增加Callback方式的接口。该接口只需描述当前函数的功能,而不涉及具体的实现细节,实现该接口的代码被称为实现。通过将接口和实现分离,符合面向对象中多态的编程思想,系统的可以通过不同的实现丰富接口的功能。
基于lab5.2的代码分析。给Linktable增加Callback方式的接口,需要两个函数接口,一个是call-in方式函数,如SearchLinkTableNode函数,其中有一个函数作为参数,这个作为参数的函数就是callback函数,如代码中Conditon函数。
在linktable.c文件中的SearchLinkTableNode()函数,该函数的参数列表中有一个参数是函数,这个作为参数的函数就是callback函数
/*
* Search a LinkTableNode from LinkTable
* int Condition(tLinkTableNode * pNode, void * args);
*/
tLinkTableNode *SearchLinkTableNode(tLinkTable *pLinkTable, int Condition(tLinkTableNode *pNode, void *args)
call-in方式的函数接口SearchLinkTableNode()增加了一个参数args,callback函数Conditon也增加了一个参数args。为了追求松散耦合,我们增加一个args参数,代替共享cmd这一方式,降低耦合度。之所以要转成void类型,就是为了更通用,是因为我们不想让底层的linktable模块知道上层用的数据类型。这样同时也不影响找节点的功能。
在menu.c文件中的FindCmd()函数调用了上述SearchLinkTableNode()函数,如下
/* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
return (tDataNode*)SearchLinkTableNode(head, SearchCondition, (void*)cmd);
}
该函数调用传入了具体实现的SearchCondition()函数作为回调函数,并通过cmd参数传递命令名称。
SearchLinkTableNode()函数实现如下,在该函数中根据传入的命令参数及搜索条件回调函数进行命令节点的查找,并在函数体内调用了回调函数Condition()。
/*
* Search 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;
}
该函数的返回类型为tLinkTableNode * 类型,而FindCmd()函数调用时把返回值强制类型转换成了tDataNode *类型。
在本实验中,使用回调函数作为参数增强了Linktable查询接口的通用性,有效地提升了接口的可重用性,这就是软件开发中解耦思想的体现。
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 10
/* data struct and its operations */
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);
}
/* 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 a cmd number > ");
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();
}
}
}
int Help()
{
ShowAllCmd(head);
return 0;
}
int Quit()
{
exit(0);
}
linktable.c文件
#include "linktable.h"
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.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;
}
/*
* Search 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;
}