打印
[其他]

C语言使用回调函数模拟委托与反射

[复制链接]
1481|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
豌豆爹|  楼主 | 2023-8-22 15:44 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 豌豆爹 于 2023-8-22 15:46 编辑

函数是C语言的核心概念。主调函数(caller)调用被调函数(callee)是一般的调用关系,如果被调函数(callee)参数包含函数指针,函数指针还可以形成多一层的调用关系,形成第三方函数的调用,专业术语称为回调(callback),通过函数指针参数调用的第三方函数称为回调函数。

回调可以让被调函数(这里是指用函数指针做函数参数的函数)的代码更加泛化或抽象,能够简单模拟其它编程语言的委托与反射语法。

1、简单模拟委托
登录后复制
//C语言简单模拟委托
//需要用的指针函数。通过用指针函数作为地址接收函数地址,以达到委托其他函数实现某方法的目的。
#include <stdio.h>
typedef void(* fun)();  //typedef 把void(*)()类型重命名为fun
void func(fun);  // 被调函数
void func_1();  // 回调函数1
void func_2();  // 回调函数2
int main() // 主函数用做主调函数
{
    func(func_1);
    fun f = func_2;
    f();
    func(func_1);
    func(func_2);
    getchar();
    return 0;
}
void func(fun f)  //fun f为地址,fun * f为f指向的地址的量或者其他
{
    printf("func\n");
    if (f != NULL)
    {
        f();
    }
}
void func_1()
{
    printf("func_1\n");
}
void func_2()
{
    printf("func_2\n");
}
/*
func
func_1
func_2
func
func_1
func
func_2
*/

使用特权

评论回复
沙发
豌豆爹|  楼主 | 2023-8-22 15:48 | 只看该作者
本帖最后由 豌豆爹 于 2023-8-22 15:49 编辑

2、简单模拟反射
(1)简单模拟反射
高级语言的反射机制,简单来说就是可以通过字符串型获取对应的类或者函数。下面用C来简单模拟反射:

#include <stdio.h>
#include <string.h>
typedef void (*callback)(void);
typedef struct {
    const char *name;
    callback fn;
}callback_t;
void f0();
void f1();
callback_t callbacks[] = {
    {"cmd0", f0},
    {"cmd1", f1},
};
void f0()   // 回调函数0
{
    printf("cmd0");
}
void f1()  // 回调函数1
{
    printf("cmd1");
}
void do_callback(const char *name)  
{
    size_t i;
    for (i = 0; i < sizeof(callbacks) / sizeof(callbacks[0]); i++) {
        if (!strcmp(callbacks.name, name)) {
            callbacks.fn();
        }
    }
}
int main()
{
    do_callback("cmd1");
    getchar();
    return 0;
}

(2)利用自定义段
gcc支持通过使用 __ attribute __ ((section())),将函数、变量放到指定的数据段中。也就是说,可以让编译器帮我们完成上例中向数组添加成员的动作。借助此机制,回调函数可以在任意文件声明,不需要修改其他文件。自定义段的起始和结束地址,可以通过变量 __ start_SECTIONNAME 和 __ stop_SECTIONNAME得到例如通过 __ attribute __ ((section("ss"))定义自定义段,其开始地址为 & __ start_ss,结束地址为 & __stop_ss。

#include <stdio.h>
#define SEC __attribute__((__section__("ss"), aligned(sizeof(void*))))
void func_1 (int a, int b)
{
    printf("%s %d %d\n", __func__, __LINE__, a+b);
}
void func_2 (int a, int b)
{
    printf("%s %d %d\n", __func__, __LINE__, a*b);
}
// 编译器会自动提供__start_ss,__stop_ss标志段ss的起止地址
extern size_t __start_ss;
extern size_t __stop_ss;
typedef struct {
    void (*p)(int, int);
} node_t;
// 结构体变量a位于自定义段ss
SEC node_t a = {
    .p = func_1,
};
SEC node_t b = {
    .p = func_2,
};
int main(int argc, char **argv)
{
   int a = 3, b = 4;
    node_t *p;
    // 遍历段ss,执行node_t结构中的p指向的函数
    for (p = (node_t *)&__start_ss; p < (node_t *)&__stop_ss;p++) {
        p->p(a, b);
        a+=1;b+=2;
    }
}
/*
func_1 6 7
func_2 10 24
*/



使用特权

评论回复
板凳
shimx| | 2023-8-22 21:36 | 只看该作者
两层楼的字体不一致哦

使用特权

评论回复
地板
HXM1593| | 2023-8-26 15:13 | 只看该作者
看不懂
(2)利用自定义段

使用特权

评论回复
5
春娇霹雳娃| | 2023-8-28 11:39 | 只看该作者

使用特权

评论回复
6
单片小菜| | 2023-8-29 14:02 | 只看该作者
C语言想要学习精通了,确实有点费劲哦。

使用特权

评论回复
7
kjkujkj21| | 2023-8-29 15:16 | 只看该作者
C语言太难了,不好弄。

使用特权

评论回复
8
AloneKaven| | 2023-8-29 21:14 | 只看该作者
C通了其他语言就好学了

使用特权

评论回复
9
tpgf| | 2023-9-8 17:34 | 只看该作者
请问什么叫做模拟委托与反射呢?

使用特权

评论回复
10
renzheshengui| | 2023-9-9 15:45 | 只看该作者
回调实际上有两种:阻塞式回调 和 延迟式回调

使用特权

评论回复
11
wakayi| | 2023-9-9 15:56 | 只看该作者
回调函数执行的流程:

主函数需要调用回调函数

中间函数登记回调函数

触发回调函数事件

调用回调函数

响应回调事件

使用特权

评论回复
12
wowu| | 2023-9-9 16:14 | 只看该作者
回调函数的机制

(1) 定义一个回调函数;

(2)提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;

(3)当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。

使用特权

评论回复
13
xiaoqizi| | 2023-9-9 16:25 | 只看该作者
调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数

使用特权

评论回复
14
木木guainv| | 2023-9-9 16:36 | 只看该作者
回调可用于通知机制,例如,有时要在程序中设置一个计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时,就需有一个特定原型的函数指针,用这个指针来进行回调,来通知我们的程序事件已经发生

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

513

主题

1960

帖子

5

粉丝