工程化编程实战callback接口学习笔记

实验环境

本次实验的环境是Windows 10+VS Code,实验用到的代码先在Linux环境下解压再拷贝到Windows中。

 

编译、运行

 先进入代码所在目录,然后输入指令“gcc -o test linktable.c menu.c”得到可执行文件“test.exe”

可以看到,我们的目录下生成了可执行文件。

工程化编程实战callback接口学习笔记_第1张图片

运行“test.exe”,并键入“quit”

跟预期的一样,quit指令不能得到正确的响应。

 

纠错

查看menu.c的main函数可以发现,当指针p为空时,会显示“This is a wrong cmd!”,所以显然给p赋值的FindCmd函数返回值为空。

int main()
{
    InitMenuData(&head); 
   /* cmd line begins */
    while(1)
    {
        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();
        }
   
    }
}

再观察FindCmd函数,其具体内容如下:

tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
    return  (tDataNode*)SearchLinkTableNode(head,SearchCondition);
}

该函数直接返回SearchLinkTableNode函数的调用结果,看来问题出在了SearchLinkTableNode上。

再查看SearchLinkTableNode函数代码如下:

tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode))
{
    if(pLinkTable == NULL || Conditon == NULL)
    {
        return NULL;
    }
    tLinkTableNode * pNode = pLinkTable->pHead;
    while(pNode != pLinkTable->pTail)
    {    
        if(Conditon(pNode) == SUCCESS)
        {
            return pNode;                    
        }
        pNode = pNode->pNext;
    }
    return NULL;
}

观察代码,首先我们可以发现,当pLinkTable或Condition为空时,返回值为空。但是不幸的是我们遇到的不是这种情况。再往下看,我们可以发现,当遍历到pLinkTable的尾结点时,也直接返回空,而没有对尾结点的Condition值进行判断。而巧合的是,由InitMenuData函数我们可以看到,指令“quit”所在的节点恰好是链表的尾结点,所以就会出现不能正常相应“quit”指令的错误。

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; 
}

解决方法很简单,只需将while循环的条件改为“while(pNode != pLinkTable->pTail->pNext)”,使尾结点也被判断即可。

重新编译、运行代码,结果如下图

 

callback接口运行机制与设计方法

通常函数的参数是一些变量,比如menu.c中的FindCmd函数:tDataNode* FindCmd(tLinkTable * head, char * cmd);

但是有一些函数的参数中可能也有函数,如linktable.c中的SearchLinkTableNode函数:tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode));其有一个参数是函数int Conditon(tLinkTableNode * pNode),这个作为参数的Condition函数即为callback函数。callback函数的运行机制,以SearchLinkTableNode函数为例,只有在其中出现调用Condition函数的语句时才会调用Condition函数,也就是SearchLinkTableNode函数中的这一句——“if(Conditon(pNode) == SUCCESS)”。也就是说,Condition函数作为参数并不是一开始就传入的。callback函数设计方法无外乎定义callback函数,并使其成为另一个函数的参数。

callback函数的作用在我看来主要是对主调函数隐藏了其实现细节,且可以与主调函数分开编写。即主调函数需要某个功能的支撑,可以先用一个callback函数作为参数,callback函数具体的实现可以稍后再来完成。有点类似于C++中的类的public成员函数。

你可能感兴趣的:(工程化编程实战callback接口学习笔记)