通过理解函数指针构建回调函数来实现mosquitto源码功能
Posted Shemesz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过理解函数指针构建回调函数来实现mosquitto源码功能相关的知识,希望对你有一定的参考价值。
1.指针(多级指针)
我们从人类起源时期讲起,说从前…hahahaha开玩笑了,但是理解这一切的基础就是先明白 指针到底是什么?
下面通过一段代码来理解复习一下指针
int main (int argc, char **argv)
{
int a = 0x12345678;
int * p1; //一级指针
int * *p2; //二级指针:指向p2指针 的指针
int * **p3; //三级指针:指向p3指针的指针 的指针
p1 = &a; //p1保存着a的地址:p1指向a
p2 = &p1; //p2保存着p1的地址:p2指向p1
p3 = &p2; //p3保存着p2的地址:p3指向p2
printf("a:%s\\n", a); //查看a的值
printf("*p1:%s\\n", *p1); //取值运算符*查看p1指向地址空间的值
printf("**p2:%s\\n", **p2); //查看p2指向的p1指向地址空间的值
printf("***p3:%s\\n", ***p3); //查看p3指向的p2指向的怕p1指向的地址空间的值
return 0;
}
打印结果:(明白了吗?就是一级一级访问想要的数据)
小技巧:(当然个人见解)
想必大家都应该玩过开心消消乐吧,其实(取值运算符*)与(取址运算符&)可以看作消消乐,这俩碰一起,就能消掉,再来一对再消,最后剩下什么,结果就是什么
2.函数指针
基于前面多级指针的理解,那么当我们将一个指针指向一个变量名的时候就表达为:*p1 =a ;那么我们是否也可以理解,当我们想用指针(*ptr)指向一个函数( int abc(int a, int b) )的函数名(abc)的时候,是否也可以表达为: int (*ptr)(int a, int b),这就是函数指针的原理,(ptr)必须要用括号,否则星号会和返回值类型int结合起来,改变返回值的类型变为指针类型!
下面我们通过一段代码来理解一下:
#include <stdio.h>
int add(int a, int b)
{
return a+b;
}
int mul(int a, int b)
{
return a*b;
}
int main (int argc, char **argv)
{
int val;
int (*ptr)(int,int); //声明这是一个指针函数,形参只声明类型即可
ptr = add; //指针指向函数变量名
val = ptr(4,5); //现在ptr就是函数指针,通过声明的函数的参数形式传值
printf("func add's val:%d\\n", val);
ptr = mul;
val = ptr(2,5);
printf("func mul's val:%d\\n", val);
return 0;
}
打印结果:
3.回调函数
什么是回调函数呢?答案其实五花八门,还是需要自己写代码才能理解更深刻
个人理解:
调用某一个函数,然后将另外一个函数作为参数传递进来叫回调函数
,英文就叫callback函数,当然理解这句话可能看的明白,但不知道具体实现呢,废话不多说,上代码,保你一眼看会!
#include <stdio.h>
int add(int a, int b)
{
return a+b;
}
int sub(int a, int b)
{
return a-b;
}
int mul(int a, int b)
{
return a*b;
}
void process_data(int a, int b, int (*callback)(int, int)) //回调函数就由函数指针来接收(想起上面函数指针写的代码了吗?就是为这个做准备)
{
printf("process data : %d\\n", callback(a, b));
}
int main (int argc, char **argv)
{
process_data(3, 4, add); //将数值与回调函数作为参数传进去,add就是回调函数
process_data(3, 4, sub);
process_data(3, 4, mul);
return 0;
}
打印结果:
4.注册函数
简答来说就是,注册这样一个函数,当条件满足的时候,就调用这个函数,我们来简单实现一下
#include <stdio.h>
#include <unistd.h>
void print_odd(void)
{
printf("odd data\\n");
}
void print_even(void)
{
printf("even data\\n");
}
void (*reg_func[1])(void); //注册这样一个函数
int main (int argc, char **argv)
{
int i;
reg_func[0] = print_odd; //奇数
reg_func[1] = print_even; //偶数
while(1)
{
reg_func[i%2](); //取余结果只有0和1,是偶数就打印偶数,是奇数就打印奇数
i++;
sleep(1);
}
return 0;
}
(其实Linux的最常见的注册函数是信号处理函数single() )
5.typedef的妙用
上面提到signal这个函数的实现,我们在Linux下来man观察一下
要实现signal()函数我们应该怎样写
看出返回值也是调用的回调函数类型,这样写又绕又难懂,可signal()就是这么实现的,那怎么好懂一点呢?这里就想到了typedef的妙用
再来看看官方man手册是怎么定义的(如出一辙),这样就十分简单易懂
那这样一来,我们也就可以把上面的注册函数改简单一点
#include <stdio.h>
#include <unistd.h>
void print_odd(void)
{
printf("odd data\\n");
}
void print_even(void)
{
printf("even data\\n");
}
typedef void (*func_t)(void);
int main (int argc, char **argv)
{
int i;
func_t reg_func[1]; //直接定义func_t类型的数组
reg_func[0] = print_odd;
eeeeeeeeeee = print_even;
while(1)
{
reg_func[i%2](); //取余结果只有0和1,是偶数就打印偶数,是奇数就打印奇数
i++;
sleep(1);
}
return 0;
}
是的这样直接将一个复杂的函数指针定义为一个类型,用的时候直接用这种类型定义出一个变量即可,十分方便
6.实现mosquitto源码功能
实现mosquitto功能前,小伙伴们可以先了解一下mosquitto函数及实现,点击下方,可以对比观看,感受更深!
mosquitto库函数
mosquitto发布与订阅的实现
首先我们的mosquitto功能的核心就是就是 struct mosquitto *mosq这个结构体我们来设计一下
- 首先,我们看mosquitto函数的时候发现所有的用户数据都是通过mosq这个结构体拿到的,所以我们肯定要设计一个指针来接收数据
struct mosqiutto
{
void *user_data;
}
- 然后我们来定义一下回调函数
- 完成回调函数定义,我们就将他加进结构体,这样设置的注册函数,当条件满足调用回调函数的时候,就可以从mosq结构体里面拿到要用的回调函数
- 这样在头文件中定义,一前一后都互相包含了对方的定义,回调函数的定义里有mosq结构体的定义,mosq结构体又包含回调函数,鸡生蛋还是蛋生鸡的世纪难题,当然编译器会有解决方法,我们只需要这样定义即可解决
下面就是我们的源码实现了 - mosquitto.c (功能函数文件)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "mosquitto.h"
struct mosquitto *mosquitto_new(void *args)
{
struct mosquitto *mosq;
mosq = malloc(sizeof(*mosq)); //开辟一段内存空间
mosq->user_data = args; //将传进来的用户数据,保存到结构体对应接口里
}
int mosquitto_connect_callback_set(struct mosquitto *mosq, mosq_callback_t cb)
{
mosq->conn_cb = cb; //传进来的回调函数保存到结构体的对应接口里去
}
int mosquitto_disconnect_callback_set(struct mosquitto *mosq, mosq_callback_t cb)
{
mosq->disc_cb = cb; 传进来的回调函数保存到结构体的对应接口里去
}
int mosquitto_loop_forever(struct mosquitto *mosq)
{
while(1)
{
mosq->conn_cb(mosq, mosq->user_data); //调用回调函数
sleep(1);
mosq->disc_cb(mosq, mosq->user_data); //调用回调函数
sleep(1);
}
}
- mosquitto.h
#ifndef _MOSQUITTO_H_
#define _MOSQUITTO_H_
struct mosquitto; //表示声明,我后面会定义这样一个结构体
typedef void (*mosq_callback_t)(struct mosquitto *, void *);
struct mosquitto
{
void *user_data; //用一个指针来接受用户数据的地址
mosq_callback_t conn_cb; //连接的回调
mosq_callback_t disc_cb; //断开的回调
};
struct mosquitto *mosquitto_new(void *args);
int mosquitto_connect_callback_set(struct mosquitto *mosq, mosq_callback_t cb);
int mosquitto_disconnect_callback_set(struct mosquitto *mosq, mosq_callback_t cb);
int mosquitto_loop_forever(struct mosquitto *mosq);
#endif
- main.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "mosquitto.h"
typedef struct mqtt_ctx_s
{
char devID[32];
int devType;
}mqtt_ctx_t;
void pub_connect_callback(struct mosquitto *mosq, void *args) //连接回调函数
{
mqtt_ctx_t *mqtt_ctx = (mqtt_ctx_t*)args;
printf("Device [%s] got connect\\n", mqtt_ctx->devID);
}
void pub_disconnect_callback(struct mosquitto *mosq, void *args) //断开的回调函数
{
mqtt_ctx_t *mqtt_ctx = (mqtt_ctx_t*)args;
printf("Device [%s] got disconnect\\n", mqtt_ctx->devID);
}
int main(int argc, char **argv)
{
mqtt_ctx_t mqtt_ctx;
struct mosquitto *mosq;
strncpy(mqtt_ctx.devID, "abc", sizeof(mqtt_ctx.devID)); //初始化结构体
mqtt_ctx.devType = 1;
mosq = mosquitto_new(&mqtt_ctx); //传入用户数据
mosquitto_connect_callback_set(mosq, pub_connect_callback); //设置回调函数,将上面设置好的回调函数传入mosq结构体,条件满足时调用回调
mosquitto_disconnect_callback_set(mosq, pub_disconnect_callback);
mosquitto_loop_forever(mosq); //在函数里循环调用mosq里设置好的回调函数
free(mosq); //释放结构体
return 0;
}
打印结果:(类似于mosquitto的循环发布)
这就是一个简单的功能模块实现,基于一些所学知识的应用,谢谢阅读!
以上是关于通过理解函数指针构建回调函数来实现mosquitto源码功能的主要内容,如果未能解决你的问题,请参考以下文章
通过理解函数指针构建回调函数来实现mosquitto源码功能