您现在的位置是:首页 > 文章详情

如何写好C语言之回调函数

日期:2019-01-16点击:444

回调函数即常说的callback,C语言开发过程中,用好回调函数可以开发出高内聚低耦合的模块代码,事实上回调函数也是解除模块之间耦合的常用方法。本文介绍几种在开发实践中用到回到函数几种情况。

遍历回调

假设现在有一个容器模块,里面存储有数据,现在我们需要遍历容器里面的数据来做一下事情(比如统计容器里大于2的元素个数),为了不暴露容器的实现细节,容器模块可以提供一个遍历所有元素的API给用户,这个API的的原型如下:

/** * @brief 遍历容器里所有的元素 * * @param p_container 容器指针 * @param cb 回调函数 * @param user_data 回调函数用户数据 * @return int 返回值 */ int container_each_element(void *p_container, void (*cb)(void *element, void *user_data), void *user_data); 

一般提供回调函数参数的API都会提供一个额外的参数user_data,这个参数会在用户调用这个API的时候由用户传入,API回调时再传给用户,这个参数可以提供很多上下文的东西,待会我们会提到,下面我们看看回到函数的原型:

/** * @brief 用户处理数据的函数 * * @param element 容器里的元素 * @param user_data 用户的回到函数数据。 */ void user_callback(void *element, void *user_data); 

回调函数第一个参数是当前遍历到的容器里的元素,第二个参数就是API回传给用户的数据,为了更清晰的了解这种使用方法,下面我们来举个例子,我们先实现一个简单的存储固定数量整型数的容器:

typedef struct { int *p; int count; }sample_container_t; sample_container_t *sample_container_create(int size) { sample_container_t *p = malloc(sizeof(sample_container_t) + size * sizeof(int)); p->p = ((char *)p) + sizeof(sample_container_t); p->count = 0; return p; } void sample_container_destory(void *p) { free(p); } //加一个数据 int sample_container_add(sample_container_t *p_container, int ele) { p_container->p[p_container->count++] = ele; } //删除最后一个数据 int sample_container_del(sample_container_t *p_container) { p_container->count--; } //遍历容器 int sample_container_each_element(sample_container_t *p_container, void (*cb)(int element, void *user_data), void *user_data) { for(int i = 0; i < p_container->count, i++) { cb(p_container->p[i], user_data); } } 

针对这个容器,我们想计算一下这个容器里大于某个数的数据有多少个,那么我们可以实现下面的回调函数:

typedef struct { int gt; int count; }user_data_t; void statistic_container_gt(int ele, void *user_data) { user_data_t *p = (user_data_t *)user_data; if (ele > p->gt) { p->count++; } } int main() { sample_container_t *p = sample_container_create(100); sample_container_add(p, 1); sample_container_add(p, 4); ///计算大于2的数的个数 user_data_t user_data = {2, 0}; sample_container_each_element(p, statistic_container_gt, &user_data); printf("The count of gt 2 is:\n", user_data.count); } 

这样做有两个好处:

  • 处理数据模块不用关心容器的实现细节,容器的任何改动都不会引起数据处理函数的修改。
  • 想对于容器直接提供API实现这样的功能,更加灵活,完全独立于业务逻辑。比如像计算容器里偶数的个数,只要在写一个回调函数就可以了,不用修改容器模块。

事件通知

在开发过程中,各个模块之间的通信是避免不了的,一般最简单的方法就是调用另外一个模块的API,告诉对方放生了什么事件。比如有一个监控一系统服务状态模块,如果发现某个服务处理不可用状态,要通知运维模块来处理这个时间。按照最直接的办法就是下面的伪代码:

int monitor_check_server_status(server_t *servers, int count) { while(1) { for(int i =0; i < count; i++) { if (is_server_unavailable(servers[i])) { ///直接调用运维模块API notify_ops_unavailable(&servers[i]); } } sleep(1); } } 

这样做基本可以实现功能,但是有两个缺点:

  • 耦合度高:直接调用运维模块的API导致两个模块产生紧耦合,实际开发过程中,如果运维模块还没有开发完成,那么我们是无法进行调试或者开发的。
  • 扩展性差:如果在后期维护过程中,需要通知更多的模块,那么我们就需要修改代码,添加更多的API调用。

那么我们引进回调函数,看看有什么样子的效果。首先这种情况下,监控服务模块应该提供两个API:

  • 注册回调函数:monitor_add_unavailable_callback()
  • 撤销回调函数: monitor_del_unavailable_callback()

按照这个模式,我们简单实现以下监控服务模块的callback版本。

typedef void (*server_unavailable_callback_t)(server_t *s); #define MAX_CALLBACKS_NUMBER 10 static server_unavaliable_t unavailable_callbacks[MAX_CALLBACK_NUMBER]; static int cb_count = 0; int monitor_add_unavailable_callback(server_unavailable_callback_t cb) { if (cb_count < MAX_CALLBACKS_NUMBER) { unavailable_callbacks[cb_count++] = cb; } } int monitor_del_unavailable_callback(server_unavailable_callback_t cb) { for(int i = 0; i < cb_count; i++) { if (unavailable_callbacks[i] == cb) { unavaiable_callbacks[i] = NULL; } } } int monitor_check_server_status(server_t *servers, int count) { while(1) { for(int i =0; i < count; i++) { if (is_server_unavailable(&servers[i])) { for (int i = 0; i < cb_count; i++) { if (unavailable_callbacks[i] != NULL) { unavailable_callbacks[i](&servers[i]) } } } } sleep(1); } } 

这样实现就完全解决了上面提到的两个问题:

  • 不依赖于任何模块,测试开发独立进行,都是只是进行联调就可以
  • 如果需要通知更多的模块,根本不需要修改代码,只是在初始化的时候添加callback就可以了。
原文链接:https://my.oschina.net/sundq/blog/3002471
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章