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

版权声明

---------------------------------------------------------------------------------------------------------------------

该文章原创于Qter开源社区(www.qter.org

作者: 女儿叫老白 (白振勇)

转载请注明出处!

---------------------------------------------------------------------------------------------------------------------

课程目录:《C++老鸟日记》目录

本套课程属于:《C++跨平台开发干货》系列课程。

----------------------------------------------------------------------------------------------------------------------

引言:

----------------------------------------------------------------------------

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

 

正文:

----------------------------------------------------------------------------

       字符串定义和字符串拼接实际是相互关联的。字符串定义是利用#把标识符替换为把字符串。比如:

#define TRACESS(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:

----------------------------------------------------------------------------

// 开关

struct TK_Breaker {

       qint32     nID;                      // 序号

       Jchar      szMingZi[NAMELEN+1];      //名字

       qint32     nDZCount;                   //开关累计动作次数

 

       TK_Breaker() {

              wID = 0;        

              szMingZi[0] = '\0';

              nDZCount = 0;            

       };

};

 

// 变压器

struct TK_Transformer  {

       int   nID;                      // 序号

       Jchar      szMingZi[NAMELEN+1];             //名字

       Jfloat      fMVA;                   //额定容量(兆伏安)

 

       TK_Transformer() {

              wID = 0;        

              szMingZi[0] = '\0;'

              fMVA             = 0.f;

       };

};

 

// 开关

static Juint16 Offset_TK_KaiGuan[] = {

       offsetof(TK_KaiGuan, wID),

       offsetof(TK_KaiGuan, szMingZi), 

       offsetof(TK_KaiGuan, nDZCount),

};

 

// 变压器

static Juint16 Offset_TK_Transformer[] = {

       offsetof(TK_Transformer, wID),

       offsetof(TK_Transformer, szMingZi),  

       offsetof(TK_Transformer, fMVA),

};

 

#define   MACRO_FUNC_GETCOMPONENT(COMPNAME)                                                                                 \

Jint32 CData::Get##COMPNAME(int nID, TK_##COMPNAME* p##COMPNAME)       \

{                                                                                                               \     

       filter.Format("ID=%d", nID);                \

       return GetDBValue(,filter, (Juint8*)p##COMPNAME, Offset_TK_##COMPNAME); \

};

// 获取数据库一条纪录

Jint32 CMSData::GetDBValue(const char* filter, Juint8* base, Juint16* offset)

{

       ......

       return ret;

}

----------------------------------------------------------------------------

在上述代码中,为了避免针对Breaker和Transformer以及更多的部件编写类似代码,特地设计了宏MACRO_FUNC_GETCOMPONENT。

针对Breaker,我们调用MACRO_FUNC_GETCOMPONENT(Breaker)。那么,宏展开后变成:

Jint32 CData::GetBreaker(int nID, 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章节),(美) Bruce Eckel  Chuck Allison著

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

女儿叫老白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值