模块的封装之无伤大雅的形式主义


1 //! \brief macro for inheritance
2 #define INHERIT(__TYPE)             __TYPE
3 #define INHERIT_EX(__TYPE, __NAME)  4             union {                 5                 __TYPE;             6                 __TYPE  __NAME;     7             };

[交流][微知识]模块的封装(三):无伤大雅的形式主义



在前面的讨论中,我们介绍了如何在必要的情况下,利用结构体和联合体在C语言中引入应用所必须的一些面向对象的

特性,例如封装、继承、派生以及覆盖。从功能性上来说,前两章的内容足够满足应用了,但从形式上来说,模块的

封装(二)中提供的继承方式在使用的时候还是有那么一点不爽,所以作为弥补一小部分有代码洁癖的人的头缺憾,今

天我们就来讨论一种在不增加任何实质性系统开销的前提下实现对象成员访问形式上的改进。

  特别提示,本帖仅用于娱乐,C++档请本着娱乐的心情吐槽和嘲讽,避免引起C档在代码效率上的反驳。形式主义

就是形式主义,没有用就是自己看着舒服

  

  首先我们看看哪里不爽了,举个例子我们定义了一个接口,用于封装一些针对存储器页(Page)的操作:

 1 //! \name memory page access interface
 2 //! @{
 3 DEF_INTERFACE(i_mem_page_t)
 4
 5     fsm_rt_t (*PageWrite)   (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
 6     fsm_rt_t (*PageRead)    (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
 7     fsm_rt_t (*PageErase)   (mem_t *ptMal, uint32_t wPageAddress);
 8     fsm_rt_t (*Erase)       (mem_t *ptMal);
 9
10 END_DEF_INTERFACE(i_mem_page_t)
11 //! @}

这个接口本身没有什么太特别的,就是定义了针对所有已Page为单位的存储器常见的四哥操作:页写,页读,页擦除和

整片擦除。接口类型叫i_mem_page_t,顾名思义就是一个针对memory page 的接口,详细内容不提也罢。接下来我

们又定义了一个接口叫i_mem_t,这个接口视图为存储器抽象一个更通用的操作方式,比如Init open close 之类的为了

方便日后的统一操作,这里不妨也定义成接口 i_mcb_t(这里mcb的意思是 memory control block的缩写),新的接口

i_mem_t就同时继承了 i_mem_page_t 和 i_mcb_t如下所示:

 1 //! \name memory control block
 2 //! @{
 3 DEF_INTERFACE(i_mcb_t)
 4
 5     fsm_rt_t        (*Init)         (mem_t *ptMal, void *ptCFG);
 6     fsm_rt_t        (*Finish)       (mem_t *ptMal);
 7     mem_info_t      (*Info)         (mem_t *ptMal);
 8     fsm_rt_t        (*Open)         (mem_t *ptMal);
 9     fsm_rt_t        (*Close)        (mem_t *ptMal);
10     mem_status_t    (*GetStatus)    (mem_t *ptMal);
11
12 END_DEF_INTERFACE(i_mcb_t)
13 //! @}
14
15 //! \name Memory Abstraction Layers
16 //! @{
17 DEF_INTERFACE(i_mem_t)
18
19     i_mcb_t CONTRL;
20     i_mem_page_t PAGE;
21     ...
22
23 END_DEF_INTERFACE(i_mem_t)
24 //! @}

好的,问题来了: 对于一小部分人来说,下面的代码是有点不可容忍的,套用一句话就是“好焦虑”,好焦虑是把?

不要怀疑,为了了凸显着莫名其妙的焦虑,我用了红色

i_mem_t *ptMEM = xxxxxx;
...
ptMEM->CONTRL.Open();    //! 打开存储器

这里多按了一个“.”,所谓多按一个点是针对那些有联想功能 的IDE的因为通过上述的方法,所谓的实现接口(Implement)

实际上是通过简单的添加接口的成员变量来实现的,在这种情况下访问继承接口的成员必须要先通过这个成员变量,

比如例子中的CONTROL。对某些人来说上面的例子代码应该支持这种形式才好:

1 i_mem_t *ptMEM = xxxxxx;
2 ...
3 ptMEM->Open();    //!< 直接打开存储器
4 ...
5 i_mcb_t *ptMCB = &(ptMEM->CONTRL);    //!< CONTRL 仍然存在

简单的来说就是既可以直接访问被继承的接口成员,又可以保留对接口的直接饮用。做到第一点并不难,只要通过下

面的代码就可以了:

 1 //! \name Memory Abstraction Layers
 2 //! @{
 3 DEF_INTERFACE(i_mem_t)
 4
 5     fsm_rt_t        (*Init)         (mem_t *ptMal, void *ptCFG);
 6     fsm_rt_t        (*Finish)       (mem_t *ptMal);
 7     mem_info_t      (*Info)         (mem_t *ptMal);
 8     fsm_rt_t        (*Open)         (mem_t *ptMal);
 9     fsm_rt_t        (*Close)        (mem_t *ptMal);
10     mem_status_t    (*GetStatus)    (mem_t *ptMal);
11
12     fsm_rt_t (*PageWrite)   (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
13     fsm_rt_t (*PageRead)    (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
14     fsm_rt_t (*PageErase)   (mem_t *ptMal, uint32_t wPageAddress);
15     fsm_rt_t (*Erase)       (mem_t *ptMal);
16
17 END_DEF_INTERFACE(i_mem_t)

看到这里的一群人就要呵呵了,我也呵呵。你会管这个叫做对接口 i_mem_apge_t 和 i_mcb_t的继承么?你骗谁?

  a、如果修改了 i_mcb_t 或者 i_mem_page_t 的内容,我们还需一起修改所有继承了他们的接口,这哪里是面

      向接口开发?这简直就是面向麻烦啊,我都不会同意。

  b、有些应用就是纯粹的面向接口(虚函数表)的,因此必须要保留对原有接口的引用能力。因此CONTROL和PAGE

    是必须要保留的。

想要同时名副其实的继承,又想有能力直接访问被继承接口的成员,就只有借助ANSI标准引入的匿名结构体了,上述的

例子的解决方案:

 1 //! \name Memory Abstraction Layers
 2 //! @{
 3 DEF_INTERFACE(i_mem_t)
 4
 5     union {
 6         struct {
 7             fsm_rt_t        (*Init)         (mem_t *ptMal, void *ptCFG);
 8             fsm_rt_t        (*Finish)       (mem_t *ptMal);
 9             mem_info_t      (*Info)         (mem_t *ptMal);
10             fsm_rt_t        (*Open)         (mem_t *ptMal);
11             fsm_rt_t        (*Close)        (mem_t *ptMal);
12             mem_status_t    (*GetStatus)    (mem_t *ptMal);
13         };
14         i_mcb_t CONTRL;
15     };
16     union {
17         struct {
18             fsm_rt_t (*PageWrite)   (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
19             fsm_rt_t (*PageRead)    (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
20             fsm_rt_t (*PageErase)   (mem_t *ptMal, uint32_t wPageAddress);
21             fsm_rt_t (*Erase)       (mem_t *ptMal);
22         };
23         i_mem_page_t PAGE;
24     };
25
26 END_DEF_INTERFACE(i_mem_t)
27 //! @}

总是觉得那里不对。。。。。尼玛,你又把接口抄了一遍

其实匿名匿名一直都是很强大的比如匿名结构体啊,匿名联合体啊,比如上面的方式可以写成下面形式

 1 //! \name Memory Abstraction Layers
 2 //! @{
 3 DEF_INTERFACE(i_mem_t)
 4
 5     union {
 6         i_mcb_t;
 7         i_mcb_t CONTRL;
 8     };
 9     union {
10         i_mem_page_t;
11         i_mem_page_t PAGE;
12     };
13
14 END_DEF_INTERFACE(i_mem_t)
15 //! @}

如果你不想暴露对原有接口的引用你甚至可以这么写

1 //! \name Memory Abstraction Layers
2 //! @{
3 DEF_INTERFACE(i_mem_t)
4
5         i_mcb_t;
6         i_mem_page_t;
7
8 END_DEF_INTERFACE(i_mem_t)
9 //! @}

肿么样,强大的cry了吧,接下来我们把形式主义进行到底,首先来定义一个红

//! \brief macro for inheritance
#define INHERIT(__TYPE)             __TYPE
#define INHERIT_EX(__TYPE, __NAME)  \
            union {                                 __TYPE;                             __TYPE  __NAME;                 };

然后自然就得到下面的代码

1 //! \name Memory Abstraction Layers
2 //! @{
3 DEF_INTERFACE(i_mem_t)  INHERIT_EX(i_mcb_t, CONTRL)  INHERIT_EX(i_mem_page_t, PAGE)
4     ...
5     ...    //! 继承后增加的成员
6     ...
7 END_DEF_INTERFACE(i_mem_t)
8 //! @}

最后我们来谈一个C语言对匿名的支持,因为这个是必要新的特性,很多C编译器并不支持。别的环节我们就不管

了,从事嵌入式开发,常见的几个C编译器比如IAR MDK GCC等都是支持的,但是要编译开关打开。粘贴下面的

代码到你的系统头文件,就可以让你的匿名代码在意思几个环境下获得支持。

 1 /* -------------------  Start of section using anonymous unions  ------------------ */
 2 #if defined(__CC_ARM)
 3   #pragma push
 4   #pragma anon_unions
 5 #elif defined(__ICCARM__)
 6   #pragma language=extended
 7 #elif defined(__GNUC__)
 8   /* anonymous unions are enabled by default */
 9 #elif defined(__TMS470__)
10 /* anonymous unions are enabled by default */
11 #elif defined(__TASKING__)
12   #pragma warning 586
13 #else
14   #warning Not supported compiler type
15 #endif
时间: 2024-10-01 08:06:26

模块的封装之无伤大雅的形式主义的相关文章

模块的封装之C语言类的继承和派生

[交流][微知识]模块的封装(二):C语言的继承和派生 在模块的封装(一):C语言的封装中,我们介绍了如何使用C语言的结构体来实现一个类的封装,并通过掩码结构体的方式实 现了类成员的保护.这一部分,我们将 在此的基础上介绍C语言类的继承和派生.其实继承和派生是一个动作的两种不同角度的表达 .当我们继承了一个基类而创造了一个新类时,派生的概念就诞生了.派生当然是从基类派生的.派生出来的类当然是继承了基类的 东西.继承和派生不是一对好基友,他们根本就是一个动作的两种不同的说法,强调动作的起始点的时候

模块的封装之C语言类的封装

[微知识]模块的封装(一):C语言类的封装 是的,你没有看错,我们要讨论的是C语言而不是C++语言中类的封装.在展开知识点之前,我首先要 重申两点: 1.面向对象是一种思想,基本与所用的语言是无关的.当你心怀面向对象时,即使使用QBasic也能写 出符合面向对象思想的代码,更不要说C语言了.举一个反例,很多人初学C++的时候,并没有掌 握面向对象的思想,活生生的把类当结构体来使用的也不在少数吧. 2.面向对象的最基本的出发点是“将数据以及处理数据的方法封装在一起”,至于继承.派生.多态之类 的则

如何为编程爱好者设计一款好玩的智能硬件(八)——LCD1602点阵字符型液晶显示模块驱动封装(中)

六.温湿度传感器DHT11驱动封装(下):如何为编程爱好者设计一款好玩的智能硬件(六)——初尝试·把温湿度给收集了(下)! 七.点阵字符型液晶显示模块LCD1602驱动封装(上):如何为编程爱好者设计一款好玩的智能硬件(七)——LCD1602点阵字符型液晶显示模块驱动封装(上) 八.LCD1602点阵字符型液晶显示模块驱动封装(中) 已经有好一阵子没写了,一方面是因为最近闲杂的事特多,另一方面(主要方面)是因为我卡在了LCD1602驱动的权衡上面——总共3个控制线和一个8位并行的数据线,放在51

Nodejs之mssql模块的封装

在nodejs中,mssql模块支持sqlserver数据库操作.今天将mssql模块的某些功能封装为一个类,方便以后调用.封装的功能有执行存储过程,执行查询语句操作等.如果本篇文章对大家有帮助,那就再好不过了! 要使用mssql模块,请先用npm加载到项目中.加载过程:打开cmd命令框,定位到项目的根目录下,输入npm install mssql --save ,然后按回车键就OK! 封装的代码如下: //导入mssql模块 var mssql=require("mssql"); v

模块的封装

一:实例化多个(swipphoto之类的,一个页面多个实例) function index(name){ this.name = name; this.init();//实例化后这个Init自动执行 } index.prototype = { init: function(){ //各种初始化方法 this.initModel(); }, initModel: function(){ console.log("初始化model"); }, getName: function(yourN

python日志模块的封装

背景: 要日志输出到文件 要每天记录一个日志文件. 1)封装model_logger.py #!/usr/bin/env  # coding:utf-8  import logging  import logging.handlers class Logger(logging.Logger):     def __init__(self, filename=None):         super(Logger, self).__init__(self)         # 日志文件名     

request请求的get/post的模块的封装

get是登录请求,post是充值请求,需要登录的cookie.#利用requests封装get和post请求import requests#不论是get,post请求都需要url,data(传递参数,mobilephone,pwd,amount),cookies,get里没有cookies,# 我们在response里添加一个cookie=None,这样get和post请求格式和内容就差不多相似,# 创建一个类classclass HttpRequest: def http_request(se

紫外线胶筑坝封装智能卡用cpu模块的方法

本发明提供了一种UV胶筑坝封装智能卡用CPU模块方法,在贴片.焊线后的载带上,用UV胶在每个器件的周围涂一圈,然后再用UV胶将坝内的芯片.金丝及其空隙填没掩盖叫填料,经过UV灯照射,固化成型并经过质量检测,实现电路封装.运用本发明方法封装的CPU模块,相比较无筑坝的封装,提高了合格率,用该模块制成的卡能承受比较高的压力和扭弯力,能更好地保护芯片.金丝,进而提高了CPU模块的可靠性. 1.一种UV胶筑坝封装智能卡用CPU模块的方法,其特征在于,所述方法包括如下步骤: a.筑坝:根据不同条带的cav

光模块有哪些封装类型?图文介绍:光模块类型和作用

在向大家介绍光模块类型之前,首先说说:光模块是什么? 光模块由光电子器件.功能电路和光接口等组成,光电子器件包括发射和接收两部分.简单的说,光模块的作用就是光电转换,发送端把电信号转换成光信号,通过光纤传送后,接收端再把光信号转换成电信号. 通过上面,我们知道什么是光模块了,可是对于不是很精通这个行业的人来说,每个光模块长得都差不多,好像是没有太多的区别.可是,事实真的是这样吗? 其实,不同类型的光模块分类区别是很大的.那么,光模块有着哪些分类呢?通过资料的分析收集,通常光模块的分类可以按一下几