|
本帖最后由 Gorgon_Meducer 于 2017-5-31 00:10 编辑
[交流][微知识]模块的封装(三):无伤大雅的形式主义
在前面的讨论中,我们介绍了如何在必要的情况下,利用结构体和联合体在C语言中引入应用所必须的一些面向对象的
特性,例如,封装、继承、派生以及覆盖。从功能性上来说,前两章的内容就足够满足应用了;但从形式上来说,模块封装
(二)中提供的类的继承方式在使用的时候还有那么一点不爽,所以,作为弥补一小部分有代码洁癖人的心头缺憾,今天我
们就来讨论一种在不增加任何实质性系统开销的前提下实现对象成员访问的形式上的改进。
特别提示:本帖仅用于娱乐,C++党请本着娱乐的心情吐槽和嘲讽,避免引起C党在代码效率上的反驳。形式主义
就是形式主义,没有用,就是自己看着舒服
首先我们来说说哪里不爽了,举个例子,我们定义了一个接口,用于封装一些针对存储器页(Page)的操作:
- //! \name memory page access interface
- //! @{
- DEF_INTERFACE(i_mem_page_t)
- fsm_rt_t (*PageWrite) (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
- fsm_rt_t (*PageRead) (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
- fsm_rt_t (*PageErase) (mem_t *ptMal, uint32_t wPageAddress);
- fsm_rt_t (*Erase) (mem_t *ptMal);
- END_DEF_INTERFACE(i_mem_page_t)
- //! @}
复制代码
这个接口本身没什么太特别的,就是定义了针对所有以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,如下所示:
- //! \name memory control block
- //! @{
- DEF_INTERFACE(i_mcb_t)
- fsm_rt_t (*Init) (mem_t *ptMal, void *ptCFG);
- fsm_rt_t (*Finish) (mem_t *ptMal);
- mem_info_t (*Info) (mem_t *ptMal);
- fsm_rt_t (*Open) (mem_t *ptMal);
- fsm_rt_t (*Close) (mem_t *ptMal);
- mem_status_t (*GetStatus) (mem_t *ptMal);
- END_DEF_INTERFACE(i_mcb_t)
- //! @}
- //! \name Memory Abstraction Layers
- //! @{
- DEF_INTERFACE(i_mem_t)
- i_mcb_t CONTRL;
- i_mem_page_t PAGE;
- ...
-
- END_DEF_INTERFACE(i_mem_t)
- //! @}
复制代码
好的,问题来了:对一小部分人来说,下面的代码是有点不可容忍的,套用一句话说就是“好焦虑”。好焦虑对吧?
不要怀疑,为了凸显这种莫名的焦虑,我特别用了红色。
- i_mem_t *ptMEM = xxxxxx;
- ...
- ptMEM->CONTRL.Open(); //! 打开存储器
复制代码
这里所谓的多按一次“.”是针对那些有联想提示功能的IDE说的,因为通过上述方法,所谓的实现接口(Implement)实际
上是通过简单的添加接口的成员变量来实现的,在这种情况下访问继承接口的成员函数必须要先通过这个成员变量,比如例子
中的CONTRL。对某些人来说上面的例子代码应该支持这种形式才好:
- i_mem_t *ptMEM = xxxxxx;
- ...
- ptMEM->Open(); //!< 直接打开存储器
- ...
- i_mcb_t *ptMCB = &(ptMEM->CONTRL); //!< CONTRL 仍然存在
复制代码
简单来说就是既可以直接访问被继承的接口的成员,又可以保留对接口的直接引用。做到第一点并不难,只要通过下面的
代码就可以了:
- //! \name Memory Abstraction Layers
- //! @{
- DEF_INTERFACE(i_mem_t)
- fsm_rt_t (*Init) (mem_t *ptMal, void *ptCFG);
- fsm_rt_t (*Finish) (mem_t *ptMal);
- mem_info_t (*Info) (mem_t *ptMal);
- fsm_rt_t (*Open) (mem_t *ptMal);
- fsm_rt_t (*Close) (mem_t *ptMal);
- mem_status_t (*GetStatus) (mem_t *ptMal);
- fsm_rt_t (*PageWrite) (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
- fsm_rt_t (*PageRead) (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
- fsm_rt_t (*PageErase) (mem_t *ptMal, uint32_t wPageAddress);
- fsm_rt_t (*Erase) (mem_t *ptMal);
- END_DEF_INTERFACE(i_mem_t)
复制代码
看到这里,一群人就要“呵呵”了,我也“呵呵”,你会管这个叫做对接口i_mcb_t和i_mem_page_t的继承么?你骗谁呢!
a . 如果修改了i_mcb_t或者i_mem_page_t的内容,我们还需要一起修改所有“所谓继承”了他们的接口,这哪里是
面向接口开发?这简直就是面向麻烦啊,我都不会同意
b. 有些应用就是纯粹的面向接口(虚函数表)的,因此必须要保留对原有接口的引用能力。因此CONTRL和PAGE是
必须要保留的。
要想同时保留名副其实的“继承”,又想有能力直接访问被继承接口的成员,就只有借助ANSI-C11标准引入的匿名结构
体了。上述例子的解决方案如下:
- //! \name Memory Abstraction Layers
- //! @{
- DEF_INTERFACE(i_mem_t)
- union {
- struct {
- fsm_rt_t (*Init) (mem_t *ptMal, void *ptCFG);
- fsm_rt_t (*Finish) (mem_t *ptMal);
- mem_info_t (*Info) (mem_t *ptMal);
- fsm_rt_t (*Open) (mem_t *ptMal);
- fsm_rt_t (*Close) (mem_t *ptMal);
- mem_status_t (*GetStatus) (mem_t *ptMal);
- };
- i_mcb_t CONTRL;
- };
- union {
- struct {
- fsm_rt_t (*PageWrite) (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
- fsm_rt_t (*PageRead) (mem_t *ptMal, uint32_t wPageAddress, void *ptBuffer);
- fsm_rt_t (*PageErase) (mem_t *ptMal, uint32_t wPageAddress);
- fsm_rt_t (*Erase) (mem_t *ptMal);
- };
- i_mem_page_t PAGE;
- };
-
- END_DEF_INTERFACE(i_mem_t)
- //! @}
复制代码
其实匿名一直是很强大的——比如匿名结构体啊,匿名联合体啊,比如,其实上面的方式可以写成如下的形式(感谢15楼剧透):
- //! \name Memory Abstraction Layers
- //! @{
- DEF_INTERFACE(i_mem_t)
- union {
- i_mcb_t;
- i_mcb_t CONTRL;
- };
- union {
- i_mem_page_t;
- i_mem_page_t PAGE;
- };
-
- END_DEF_INTERFACE(i_mem_t)
- //! @}
复制代码
如果你不想保留对原有接口的引用,你甚至可以这么写:
- //! \name Memory Abstraction Layers
- //! @{
- DEF_INTERFACE(i_mem_t)
-
- i_mcb_t;
- i_mem_page_t;
-
- END_DEF_INTERFACE(i_mem_t)
- //! @}
复制代码
肿么样,强大的cry了吧。接下来,让我们把形式主义进行到底,首先来定义一个宏:
- //! \brief macro for inheritance
- #define INHERIT_EX(__TYPE, __NAME) \
- union { \
- __TYPE __NAME; \
- __TYPE; \
- };
- /*! \note When deriving a new class from a base class, you should use INHERIT
- * other than IMPLEMENT, although they looks the same now.
- */
- #define INHERIT(__TYPE) INHERIT_EX(__TYPE, base__##__TYPE)
- /*! \note You can only use IMPLEMENT when defining INTERFACE. For Implement
- * interface when defining CLASS, you should use DEF_CLASS_IMPLEMENT
- * instead.
- */
- #define IMPLEMENT(__INTERFACE) INHERIT_EX(__INTERFACE, base__##__INTERFACE)
- /*! \note if you have used INHERIT or IMPLEMENT to define a CLASS / INTERFACE,
- you can use OBJ_CONVERT_AS to extract the reference to the inherited
- object.
- \*/
- #define OBJ_CONVERT_AS(__OBJ, __INTERFACE) (__OBJ.base__##__INTERFACE)
复制代码
然后自然就得到以下的代码:
- //! \name Memory Abstraction Layers
- //! @{
- DEF_INTERFACE(i_mem_t) INHERIT(i_mcb_t) INHERIT(i_mem_page_t)
- ...
- ... //! 继承后增加的成员
- ...
- END_DEF_INTERFACE(i_mem_t)
- //! @}
复制代码
对于i_mem_t的对象,如果我们想访问其继承的接口,可以通过下面的代码来实现:
- i_mem_t *ptMEM = .....;
- i_mcb_t *ptMCB = &OBJ_CONVERT_AS((*ptMEM), i_mcb_t);
- ptMCB->Init(...)
- ...
复制代码
或者
- i_mem_t *ptMEM = .....;
- i_mcb_t *ptMCB = REF_OBJ_AS((*ptMEM), i_mcb_t);
- ptMCB->Init(...)
- ...
复制代码
当然,普通情况下,我们只要直接用ptMEM访问Init()方法就好了。上面的方法的方法是为面向接口开发所余留的能力。
最后我们来谈一个C语言对“匿名”的支持问题。因为这是一个比较新的特性,很多C编译器并不默认支持。别的环境我们
就不管了,从事嵌入式开发,常见的几个C编译环境比如IAR,ARM_MDK,GCC等等都是支持的,但需要编译开关打开。
粘贴下面的代码到你的系统级头文件中,就可以让你的匿名代码在以上几个环境下都获得支持:
- /* ------------------- Start of section using anonymous unions ------------------ */
- #if defined(__CC_ARM)
- //#pragma push
- #pragma anon_unions
- #elif defined(__ICCARM__)
- #pragma language=extended
- #elif defined(__GNUC__)
- /* anonymous unions are enabled by default */
- #elif defined(__TMS470__)
- /* anonymous unions are enabled by default */
- #elif defined(__TASKING__)
- #pragma warning 586
- #else
- #warning Not supported compiler type
- #endif
复制代码
全文完
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
|