出神入化的枚举结构体函数声明

瞻仰完大神改STM32中断向量表的代码,今天我们来看看“大神”写的枚举、结构、函数声明,还是一样的配方,一样的烧脑。

少废话,先放代码。第一段是枚举,第二段是函数,第三段是对一个数组的初始化。

//==============================================================================
#define UTIL_EVENT_Define(T,X)    
#define UTIL_STATE_Define(T,X)    UTIL_STATE_ENUM(T,X),    
enum
{
#include "APP_PROC_STATE_M_NET_SETUP_define.h"
};
#undef UTIL_STATE_Define  
#undef UTIL_EVENT_Define
//==============================================================================

//==============================================================================
#define UTIL_STATE_Define(T,X)    UTIL_STATE_FUNC_Extern(T,X) 
#define UTIL_EVENT_Define(T,X)
#include "APP_PROC_STATE_M_NET_SETUP_define.h"
#undef UTIL_STATE_Define 
#undef UTIL_EVENT_Define
//==============================================================================

//==============================================================================
#define UTIL_STATE_Define(T,X)    UTIL_STATE_FUNC_ITEM_IN_TAB(T,X),
#define UTIL_EVENT_Define(T,X)
const __pgm __state_func_t APP_PROC_STATE_M_NET_SETUP_TAB[]=
{
#include "APP_PROC_STATE_M_NET_SETUP_define.h"
};
#undef UTIL_STATE_Define 
#undef UTIL_EVENT_Define

这个APP_PROC_STATE_M_NET_SETUP_define.h 头文件内容比较多,但是每一项都大同小异,我这里放一部分出来。

UTIL_STATE_Define(APP_PROC_STATE_M_NET_SETUP_t,BEGIN)
UTIL_STATE_Define(APP_PROC_STATE_M_NET_SETUP_t,END)
UTIL_STATE_Define(APP_PROC_STATE_M_NET_SETUP_t,AT_E0)

UTIL_STATE_Define(APP_PROC_STATE_M_NET_SETUP_t,AT_CPIN)

不知道各位看官在第一次阅读这个代码的时候,有没有立马理解里面的门道。能写出这样代码的人,对编译器的理解,C语言的功力都是深厚的,不然也不能被叫做大神。接下来我就来好好理解理解里面的门道。

首先是对#include的理解。平时我们都知道要获取一个模块对外的函数声明,结构体枚举定义,变量声明等,要include那个模块的头文件。这只是会用,其实include的本质,是将指定头文件的内容替换掉#include这句,当然这个操作是编译器去执行的。

举个例子

//ModuleA.h

extern uint8_t Test;
uint8_t GetFun(void);
//ModuleB.c


#include "ModuleA.h"


....
....
....

我们在模块B的c文件中包含了模块A的头文件。编译器在编译的时候,首先会把模块B的c文件变成这样,然后才进行编译。

//ModuleB.c

extern uint8_t Test;
uint8_t GetFun(void);



...
...
...

宏定义的本质跟#include是一样的,编译器在编译之前,会把代码里的宏替换成定义的内容。

理解了这些,我们拿一开始那段代码里的枚举定义来看看

#define UTIL_EVENT_Define(T,X)    
#define UTIL_STATE_Define(T,X)    UTIL_STATE_ENUM(T,X),    
enum
{
#include "APP_PROC_STATE_M_NET_SETUP_define.h"
};
#undef UTIL_STATE_Define  
#undef UTIL_EVENT_Define

也就是说,编译器编译的时候会把APP_PROC_STATE_M_NET_SETUP_define.h文件里的内容“粘贴”到枚举定义里,我们试着还原一下“粘贴”后的效果

#define UTIL_EVENT_Define(T,X)    
#define UTIL_STATE_Define(T,X)    UTIL_STATE_ENUM(T,X),    
enum
{
UTIL_STATE_Define(APP_PROC_STATE_M_NET_SETUP_t,BEGIN)
UTIL_STATE_Define(APP_PROC_STATE_M_NET_SETUP_t,END)
UTIL_STATE_Define(APP_PROC_STATE_M_NET_SETUP_t,AT_E0)

UTIL_STATE_Define(APP_PROC_STATE_M_NET_SETUP_t,AT_CPIN)
};
#undef UTIL_STATE_Define  
#undef UTIL_EVENT_Define

这个时候留意一下,枚举定义之前,对UTIL_STATE_Define这个宏进行定义,实际情况应该是这样

enum
{
UTIL_STATE_ENUM(APP_PROC_STATE_M_NET_SETUP_t,BEGIN),
UTIL_STATE_ENUM(APP_PROC_STATE_M_NET_SETUP_t,END),
UTIL_STATE_ENUM(APP_PROC_STATE_M_NET_SETUP_t,AT_E0),

UTIL_STATE_ENUM(APP_PROC_STATE_M_NET_SETUP_t,AT_CPIN),
};

那这个UTIL_STATE_ENUM宏又是啥定义,我在另外一个头文件里找到了。

#define UTIL_STATE_ENUM(T,X)              STATE_ENUM_##T##_##X

好了,那么枚举的终极展开就是这样,名字真的。。。哎,好长,好长。

enum
{
STATE_ENUM_APP_PROC_STATE_M_NET_SETUP_t_BEGIN,
STATE_ENUM_APP_PROC_STATE_M_NET_SETUP_t_END,
STATE_ENUM_APP_PROC_STATE_M_NET_SETUP_t_AT_E0,
STATE_ENUM_APP_PROC_STATE_M_NET_SETUP_t_AT_CPIN,
};

这份代码接下来还有个地方有意思,就是刚刚这个APP_PROC_STATE_M_NET_SETUP_define.h文件不仅仅被用在了枚举定义里,还被用在函数声明和数组初始化里。这是怎么做到的呢。

我们来看一下,函数声明部分。

#define UTIL_STATE_Define(T,X)    UTIL_STATE_FUNC_Extern(T,X) 
#define UTIL_EVENT_Define(T,X)
#include "APP_PROC_STATE_M_NET_SETUP_define.h"
#undef UTIL_STATE_Define 
#undef UTIL_EVENT_Define

真鸡贼,在函数定义之前,把UTIL_STATE_Define这个宏重新定义了,这个宏就是头文件里的那个。UTIL_STATE_FUNC_Extern这个宏的定义如下。

#define UTIL_STATE_FUNC(T,X)              STATE_FUNC_##T##_##X
#define UTIL_STATE_FUNC_INLINE(T,X)       STATE_FUNC_##T##_##X##_
#define UTIL_STATE_FUNC_Extern(T,X)       void UTIL_STATE_FUNC(T,X)(UTIL_EVENT_STATE_t *p_es);

又是各种拼接宏,看得头晕,我就不展开了,总之他通过这种方法,定义了一堆的函数,并且函数的名字靠编译器编译的时候去拼接。更鸡贼的是,他在函数定义之后,还#undef了之前定的宏,防止后面用错。

最后,我们来看一下数组的初始化。

#define UTIL_STATE_Define(T,X)    UTIL_STATE_FUNC_ITEM_IN_TAB(T,X),
#define UTIL_EVENT_Define(T,X)
const __pgm __state_func_t APP_PROC_STATE_M_NET_SETUP_TAB[]=
{
#include "APP_PROC_STATE_M_NET_SETUP_define.h"
};
#undef UTIL_STATE_Define 
#undef UTIL_EVENT_Define

还是一样的手法,我们只有看了UTIL_STATE_FUNC_ITEM_IN_TAB这个宏,才知道数组是怎么初始化的。

#define UTIL_STATE_FUNC_ITEM_IN_TAB(T,X)  [UTIL_STATE_ENUM(T,X)]=UTIL_STATE_FUNC(T,X)

这里用到了之前枚举和函数定义的拼接宏。枚举,函数声明,数组初始化,合起来总的想法就是,定义一个函数指针数组,用枚举做索引,把函数名字写到数组的初始化。

确实,通过这一顿操作,少写了很多代码,这些代码都靠编译器自己完成了。但是可读性嘛,谁看谁知道。

看到这里,可能大家跟我有一样的疑问,这么花式地把枚举,函数给定义了,用的时候咋办咧。无他,还是靠拼接宏。比如要用到枚举,只能是这样

UTIL_STATE_ENUM(APP_PROC_STATE_M_NET_SETUP_t,BEGIN)

这样的代码,首先可读性十分差劲,滥用拼接宏,会频繁打断阅读的思路,最可恶的是函数的名字用拼接,在调试的时候往往无法直接跳转到定义,想要搜索也无从下手,效率低下。函数名字用拼接还有一个十分恶劣的影响,就是编译出错的时候,错误信息里函数的名字是编译器对宏进行完全展开后的名字,你看到函数的名字会非常的陌生和懵逼,肯定无法第一时间就想起来是哪个函数。这样的代码,也就适合拿来提升你对编译器和C语言的理解了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值