找回密码
 立即注册
收起左侧

C++老鸟日记032 预处理宏并非一无是处

0
回复
5534
查看
[复制链接]
累计签到:41 天
连续签到:1 天
来源: 原创 2018-9-25 16:49:02 显示全部楼层 |阅读模式

马上注册,查看详细内容!注册请先查看:注册须知

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 baizy77 于 2018-10-1 21:00 编辑

版权声明
---------------------------------------------------------------------------------------------------------------------
该文章原创于Qter开源社区(www.qter.org
作者: 女儿叫老白 (白振勇)
转载请注明出处!
---------------------------------------------------------------------------------------------------------------------
本套课程属于:《C++跨平台开发干货》系列课程。
-----------------------------------------------------------------------------

引言:
----------------------------------------------------------------------------
       在前面的讨论中,我们使用内联函数代替了预处理宏来减少函数压栈;使用const常量或者constexpr来代替宏常量定义,如此看来,预处理宏好像没啥卵用呢。其实不然,预处理红并非一无是处。我们来看一下预处理宏的几个特征:字符串定义、字符串拼接和标志黏贴。

正文:
----------------------------------------------------------------------------
       字符串定义和字符串拼接实际是相互关联的。字符串定义是利用#把标识符替换为把字符串。比如:
#defineTRACESS(s)  (cerr << __FILE__<< ", line: "<< __LINE__ << ", "<<#s << endl;)
然后在代码中调用它:
TRACESS(f(3));
我们来看一下这个预处理宏:
1,  s是标识符
2,  cerr是std的标准错误输出(一般指终端)
3, __FILE__宏和__LINE__宏是标准宏,前者输出当前执行的代码的文件名,后者输出当前代码行数。
4, #s用来把标识符转换为字符串输出。比如下面的输出中,f(3)被原封不动的输出到了终端。
它的运行输出如下:
d:\xingdianketang\project\gui\src\chapter00\syncdir\main.cpp,line: 33, f(3)

那么标志黏贴是咋回事呢?标志黏贴实际是将字符串标志替换为代码中的标志,它的语法为:##标志。
比如:
#define COMSTRUCT(COMPO)  TK_##COMPO
如果我们调用COMSTRUCT(Breaker),展开后,变成:
       TK_Breaker

在项目中的应用如下列代码中的宏MACRO_FUNC_GETCOMPONENT:
----------------------------------------------------------------------------
// 开关
structTK_Breaker {
       qint32     nID;                      // 序号
       Jchar      szMingZi[NAMELEN+1];      //名字
       qint32     nDZCount;                   //开关累计动作次数

       TK_Breaker()  {
              wID = 0;         
              szMingZi[0] = '\0';
              nDZCount = 0;            
       };
};

// 变压器
structTK_Transformer  {
       int   nID;                      // 序号
       Jchar      szMingZi[NAMELEN+1];             //名字
       Jfloat      fMVA;                   //额定容量(兆伏安)

       TK_Transformer()  {
              wID = 0;         
              szMingZi[0] = '\0;'
              fMVA              = 0.f;
       };
};

// 开关
static Juint16Offset_TK_KaiGuan[] = {
       offsetof(TK_KaiGuan, wID),
       offsetof(TK_KaiGuan, szMingZi),  
       offsetof(TK_KaiGuan, nDZCount),
};

// 变压器
static Juint16Offset_TK_Transformer[] = {
       offsetof(TK_Transformer, wID),
       offsetof(TK_Transformer, szMingZi),   
       offsetof(TK_Transformer, fMVA),
};

#define   MACRO_FUNC_GETCOMPONENT(COMPNAME)                                                                                 \
Jint32 CData::Get##COMPNAME(intnID, TK_##COMPNAME* p##COMPNAME)       \
{                                                                                                               \      
       filter.Format("ID=%d", nID);                \
       return GetDBValue(,filter,(Juint8*)p##COMPNAME, Offset_TK_##COMPNAME); \
};
// 获取数据库一条纪录
Jint32CMSData::GetDBValue(const char* filter, Juint8* base, Juint16* offset)
{
       ......
       return ret;
}
----------------------------------------------------------------------------
在上述代码中,为了避免针对Breaker和Transformer以及更多的部件编写类似代码,特地设计了宏MACRO_FUNC_GETCOMPONENT。
针对Breaker,我们调用MACRO_FUNC_GETCOMPONENT(Breaker)。那么,宏展开后变成:
Jint32 CData::GetBreaker(intnID, TK_Breaker* pBreaker) \
{                                                                                                               \      
       filter.Format("ID=%d", nID);                \
       return GetDBValue(filter,(Juint8*)pBreaker, Offset_TK_Breaker);    \
};
如果我们调用MACRO_FUNC_GETCOMPONENT(Transformer)。那么,宏展开后变成:
Jint32 CData::GetTransformer(int nID, TK_Transformer * pTransformer)      \
{                                                                                                               \      
       filter.Format("ID=%d", nID);                \
       return GetDBValue(filter,(Juint8*) pTransformer, Offset_TK_Transformer);   \
};
这样,我们就不用针对不同的结构体来编写不同的代码了,而这些代码大部分都重复。


结语:
----------------------------------------------------------------------------
       看来,预处理宏还是有它的用武之地的,前提是我们使用得当。

参考资料
----------------------------------------------------------------------------
《C++编程思想》两卷合订本中文版(9.6章节),(美) BruceEckel  Chuck Allison著


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了