在实现线程池的功能时,遇到一个c语言模块开发的技巧,愚笨的自己耗费了好大的精力去理解相关的代码逻辑,这里做了相关的整理:
1:对这个思路的理解:
C语言是面向过程开发的,但是可以用一定的技巧,参考C++面向对象的技巧,有效的组织代码架构。
基于此思路,实现下面一套适用于c语言的模块开发的技巧:
1:定义基础的结构,确定实现包裹函数。
2:定义业务结构体,一个void*的指针和需要的数据结构。
3:实例化一个变量,根据基础的结构,包含了业务结构体数据结构大小和实际的执行函数。
4:调用包裹函数,传入实例化的对象去初始化,获取到业务结构体==》真正的执行函数地址+数据结构
5:调用其他包裹函数,传入实例化的对象可以实现调用。
**重点:**业务实际的数据结构和 底层包裹函数实例化后的结构的关系,由包裹函数操作
2:对实现思路的解释:
1:思考原理:
程序=算法+数据结构,在实现业务功能时,我们一般关注的有两个点:数据结构和执行函数。
==》可以得出,我们如果获取到程序执行需要的相关数据结构和执行函数,则可以控制整个流程。
==》为了控制每个业务流程相关的数据结构和执行函数,即,每个程序(实例)需要关注的自身的结构定义 ,如下文的Base_Struct 和 Base_Struct_expand实例的定义
==》数据结构一般需要我们手动的初始化,一般需要申请内存,需要关注结构类型和大小。
相关的执行函数,我们可以通过指针指向的方式实现获取。
==》流程中的控制体: 保存了函数指针和相关初始化过的数据结构的一个对象。 即参考源码中的TEST_MODULE,void* _的定义指向真正的函数实例对象moudle_t
这是同一类的数据结构和函数,实现同一种业务功能的封装(类比C++中的类)。
2:实现
1:入参 : 数据结构的大小和实际执行的函数的实例化 ==》结构定义参考Base_Struct和Base_Struct_expand
2:返回值/传入控制体:返回指向执行函数指针地址和数据结构内容的结构 ==》参考 TEST_MODULE的定义
3:实际函数的执行: 函数的触发不再依赖传统的调用,而是需要专门的包裹函数去调用执行
==》参考这里的New, Delete,Show
==》注意New函数中的逻辑,申请内存和把结构体的首地址实际指向入参参数保存的函数指针位置。
4:流程控制:New的时候构造保存了函数指针和需要数据结构的结构指针(返回值)。
其他函数的执行传入New函数返回的结构指针。
3:扩展使用:
1:这样使用的好处:
专门的函数实现了同一类型的同一种功能函数的调用,这样:
调用者不必关注实际的实例的函数,
实现者不用关心如何调用。
实现了调用者和和实现者之间的解耦,是一种可扩展的模块开发技巧。
2:扩展使用:
1:nginx模块开发
2:基于c语言实现的单例模式,工厂模式等一系列设计模式的封装。
3:相关测试源代码:
base.h:
#ifndef _BASE_H_ #define _BASE_H_ #include <stdarg.h> //统一的函数执行体 //size 保存了子结构地址和需要的数据结构 typedef struct { size_t size; void* (*ctor)(void*_self, va_list *params); void* (*dtor)(void*_self); }Base_Struct; void* New(const void* _class, ...); void Delete(void* _class); typedef struct { size_t size; void* (*ctor)(void*_self, va_list *params); void* (*dtor)(void*_self); void* (*show)(void*_self); }Base_Struct_expand; void Show(void* _class); #endif
base.c
#include <stdlib.h> #include <assert.h> #include "base.h" void* New(const void* _class, ...) { const Base_Struct *class = _class; void * p = calloc(1, class->size); assert(p); //注意 这里是重点 保存了实际的执行操作结构体的地址 *(const Base_Struct**)p = class; //两层取值可以取到实际结构体位置 if(class->ctor) { va_list params; va_start(params, _class); p = class->ctor(p, ¶ms); va_end(params); } return p; } //取地址里存的值 找到对应的地址执行 void Delete(void* _class) { const Base_Struct **class = _class; if(_class && (*class) && (*class)->dtor) { _class = (*class)->dtor(_class); } free(_class); } void Show(void* _class) { const Base_Struct_expand ** class = _class; if (_class && (*class) && (*class)->show) { (*class)->show(_class); } }
test_module.h
#ifndef _TEST_MODULE_H_ #define _TEST_MODULE_H_ #include <stdarg.h> //直接在基础定义模型使用,不扩展其他的函数接口 //定义我们的结构体 我们实际的数据 typedef struct _t_test_struct { int id; char name[8]; }TEST_STRUCT; //定义符合结构的结构体 typedef struct _t_test_module { const void *_; TEST_STRUCT * data; }TEST_MODULE; //结构定义入参 以此为标准申请内存和指针指向 extern const void *module_test; #endif
test_module.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "base.h" #include "test_module.h" //new函数真正调用这个函数 这里做真正初始化 //new函数实际做了这里真正的指向 和数据的传递 //可以通过传参初始化这里的结构体 static void *test_module_create(void *_self, va_list *params) { TEST_MODULE *data_t = (TEST_MODULE*)_self; //这个的实际指向就是moudle_t定义 TEST_STRUCT * data = (TEST_STRUCT*)malloc(sizeof(TEST_STRUCT)); data_t->data = data; int arg = va_arg(params, int); printf("get para is %d. \n",arg); data->id = arg; memcpy(data->name, "mytest", 6); printf("test_module_create :%d, %s \n", data->id, data->name); return data_t; } //Delete 函数真正掉这个函数 做对应的销毁 static void *test_module_destory(void *_self) { TEST_MODULE *data_t = (TEST_MODULE*)_self; //目的是销毁create中对应的结构 printf("test_module_destory :%d, %s \n", data_t->data->id, data_t->data->name); if(data_t->data != NULL) { free(data_t->data); printf("free data success \n"); } return data_t; } //按照真正的结构进行组织 static const Base_Struct moudle_t = { sizeof(TEST_MODULE), test_module_create, test_module_destory, }; //把该结构地址共享出去,基类通过地址强行转换执行到这里的函数 const void * module_test = &moudle_t;
test_module_expand.h
#ifndef _TEST_MODULE_EXPAND_H_ #define _TEST_MODULE_EXPAND_H_ #include <stdarg.h> //结构定义入参 以此为标准申请内存和指针指向 extern const void *module_test_expand; #endif
test_module_expand.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "base.h" #include "test_module.h" //new函数真正调用这个函数 这里做真正初始化 //new函数实际做了这里真正的指向 和数据的传递 //可以通过传参初始化这里的结构体 static void *test_module_expand_create(void *_self, va_list *params) { TEST_MODULE *data_t = (TEST_MODULE*)_self; TEST_STRUCT * data = (TEST_STRUCT*)malloc(sizeof(TEST_STRUCT)); int arg = va_arg(params, int); data->id = arg; memcpy(data->name, "mytest", 6); printf("test_module_create :%d, %s \n", data->id, data->name); data_t->data = data; return data_t; } //Delete 函数真正掉这个函数 做对应的销毁 static void *test_module_expand_destory(void *_self) { const Base_Struct_expand **class = _self; if(_self && (*class) && (*class)->dtor) { _self = (*class)->show(_self); } //目的是销毁create中对应的结构 TEST_MODULE *data_t = (TEST_MODULE*)_self; printf("test_module_expand_destory :%d, %s \n", data_t->data->id, data_t->data->name); if(data_t->data != NULL) { free(data_t->data); printf("free data success \n"); } return data_t; } static void *test_module_expand_show(void *_self) { TEST_MODULE *data_t = (TEST_MODULE*)_self; //目的是销毁create中对应的结构 printf("test_module_expand_show :%d, %s \n", data_t->data->id, data_t->data->name); return data_t; } //按照真正的结构进行组织 static const Base_Struct_expand moudle_t = { sizeof(TEST_STRUCT), test_module_expand_create, test_module_expand_destory, test_module_expand_show, }; //把该结构地址共享出去,基类通过地址强行转换执行到这里的函数 const void * module_test_expand = &moudle_t;
main.c
#include <stdio.h> #include <stdlib.h> #include "test_module.h" #include "test_module_expand.h" #include "base.h" int main() { //根据module_test 去构造 第二个参数实际上是对象实际构造函数的参数 //会重新申请需要用的数据结构,共用一套函数方法 void *test = New(module_test, 3); //这里可以多个参数,数据的初始化 void *test1 = New(module_test_expand, 2); Show(test1); Delete(test); Delete(test1); return 0; }
测试执行结果:
ubuntu@ubuntu:~/test/cmodule$ gcc *.c -o main ubuntu@ubuntu:~/test/cmodule$ ./main get para is 3. test_module_create :3, mytest test_module_create :2, mytest test_module_expand_show :2, mytest test_module_destory :3, mytest free data success test_module_expand_show :2, mytest test_module_expand_destory :2, mytest free data success