内容简介:今天在开发的过程中需要把两个类似产品的代码进行合并,减少代码的维护量,同时可以减少物料的维护成本。两个产品只是一个通信用结构体内的元素有些区别,结构体相同元素的便宜地址发生了变化,同时增加了一些元素。在设计之初应该考虑到升级的问题,应该做到通信地址兼容,不改变原来的通信地址,但是由于前人已设计好了,且工程庞大,不易再修改。
今天在开发的过程中需要把两个类似产品的代码进行合并,减少代码的维护量,同时可以减少物料的维护成本。
两个产品只是一个通信用结构体内的元素有些区别,结构体相同元素的便宜地址发生了变化,同时增加了一些元素。
在设计之初应该考虑到升级的问题,应该做到通信地址兼容,不改变原来的通信地址,但是由于前人已设计好了,且工程庞大,不易再修改。
在此基础上想到了,让程序动态的去动态的确定使用哪个结构体去使用,正好,这个结构体一个固定的便宜量中有一个产品ID能够用来区分是哪一种产品。这个恰好是反射机制,在此记录学习一下反射机制
反射机制
计算机中的反射,是在运行的时候来自我检查,并对内部成员进行操作。就是说这个变量的类型可以动态的改变,在运行的时候确定它的作用,很多高级编程语言中有这些机制:Python,lua,c#,java都自带有这个机制。下面以 lua 作为例子
local AllTypes = { Type1 = 1, Type2 = 2, Type3 = 3, } local typeClsHash = {} typeClsHash[AllTypes.Type1] = Cls1 typeClsHash[AllTypes.Type2] = Cls2 typeClsHash[AllTypes.Type3] = Cls3 local theType = AllTypes.Type2 local cls = typeClsHash[theType] local instance = cls:new() instance:doSth()
因为没有指定变量的类型,所以可以把任意类型赋值给他,在运行的时候检测当前的类型。
c实现反射机制
高级语言的反射机制,简单来说就是可以通过字符串型获取对应的类或者函数。但是 c语言 没有这个机制,需要自己动手来实现。
基础形式,c语言结构化编程基础实现
1)声明
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() { } void f1() { }
2)调用
void do_callback(const char *name) { size_t i; for (i = 0; i < sizeof(callbacks) / sizeof(callbacks[0]); i++) { if (!strcmp(callbacks[i].name, name)) { return callbacks[i].fn(); } } }
这种方式的不便之处在于,当需要映射的函数因分散在不同文件时,每增加一个新的映射都需要修改这个数组,以及头文件。
利用自定义段
gcc支持通过使用__attribute__((section())),将函数、变量放到指定的数据段中。
也就是说,可以让编译器帮我们完成1中向数组添加成员的动作。
借助此机制,回调函数可以在任意文件申明,不需要修改其他文件。
内核驱动初始化函数的遍历执行就采用了这种方法。
下面是init/main.c对于初始化函数的遍历调用
extern initcall_t __initcall_start[]; extern initcall_t __initcall0_start[]; extern initcall_t __initcall1_start[]; extern initcall_t __initcall2_start[]; extern initcall_t __initcall3_start[]; extern initcall_t __initcall4_start[]; extern initcall_t __initcall5_start[]; extern initcall_t __initcall6_start[]; extern initcall_t __initcall7_start[]; extern initcall_t __initcall_end[]; static initcall_t *initcall_levels[] __initdata = { __initcall0_start, __initcall1_start, __initcall2_start, __initcall3_start, __initcall4_start, __initcall5_start, __initcall6_start, __initcall7_start, __initcall_end, }; //这些值都是在lds链接脚本中得到的数据 /* Keep these in sync with initcalls in include/linux/init.h */ static char *initcall_level_names[] __initdata = { "early", "core", "postcore", "arch", "subsys", "fs", "device", "late", }; static void __init do_initcall_level(int level) { extern const struct kernel_param __start___param[], __stop___param[]; initcall_t *fn; strcpy(static_command_line, saved_command_line); parse_args(initcall_level_names[level], static_command_line, __start___param, __stop___param - __start___param, level, level, &repair_env_string); for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) do_one_initcall(*fn); } static void __init do_initcalls(void) { int level; for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) do_initcall_level(level); }
配合arch/xxx/kernel/vmlinux.lds
. = ALIGN(4096); /* Init code and data */ __init_begin = .; . = ALIGN(4096); .init.text : AT(ADDR(.init.text) - 0) { _sinittext = .; *(.init.text) *(.cpuinit.text) *(.meminit.text) _einittext = .; } .init.data : AT(ADDR(.init.data) - 0) { *(.init.data) *(.cpuinit.data) *(.meminit.data) *(.init.rodata) *(.cpuinit.rodata) *(.meminit.rodata) . = ALIGN(32); __dtb_start = .; *(.dtb.init.rodata) __dtb_end = .; . = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .; __initcall_start = .; *(.initcallearly.init) __initcall0_start = .; *(.initcall0.init) *(.initcall0s.init) __initcall1_start = .; *(.initcall1.init) *(.initcall1s.init) __initcall2_start = .; *(.initcall2.init) *(.initcall2s.init) __initcall3_start = .; *(.initcall3.init) *(.initcall3s.init) __initcall4_start = .; *(.initcall4.init) *(.initcall4s.init) __initcall5_start = .; *(.initcall5.init) *(.initcall5s.init) __initcallrootfs_start = .; *(.initcallrootfs.init) *(.initcallrootfss.init) __initcall6_start = .; *(.initcall6.init) *(.initcall6s.init) __initcall7_start = .; *(.initcall7.init) *(.initcall7s.init) __initcall_end = .; __con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .; __security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .; }
利用链接脚本可以得到每段的起始地址,然后在一段中遍历执行每一个初始化函数
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Go语言反射之反射调用
- Go语言反射之类型反射
- Go语言反射之值反射
- 模块讲解----反射 (基于web路由的反射)
- 装饰器与元数据反射(4)元数据反射
- .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
HTML 压缩/解压工具
在线压缩/解压 HTML 代码
RGB HSV 转换
RGB HSV 互转工具