浏览量:118次
从上一次写文章到现在已经快2个月了,为啥没更新,因为太忙了!
每天都在为了实现一些麻烦的功能而心急火燎,总是没办法静下心来写文章.所以一直拖延.
对于宏的一些个人用法其实早就想写出来了,碍于时间关系一直拖到现在.
宏在很多人看来就是一个危险的存在.不过在我看来宏真的是一个不可或缺的好东西.之所以被嗤之以鼻,那是有很多人不会正确使用宏,导致项目中宏定义乱飞,各种难以理解的宏嵌套,宏展开编译失败的复杂提示信息.
1.标识当前平台.
因为我的项目需要在windows和linux都要能正常运行,我在windows进行开发,linux上运行.我只是不喜欢在linux上写代码而已,用惯了windows的vs,不太愿意再花太多功夫去习惯别的系统.而且是linux这样的对新手不友好的系统.
先显式定义三个平台标识宏
#definePLATFORM_WINDOWS0#definePLATFORM_LINUX1#definePLATFORM_ANDROIDPLATFORM_LINUX//正在运行的平台标识#ifdefWINDOWS#defineRUN_PLATFORMPLATFORM_WINDOWS#endif#ifdefLINUX#defineRUN_PLATFORMPLATFORM_LINUX#endif#ifndefRUN_PLATFORM#defineRUN_PLATFORM-1#error"wrongplatform!"#endif因为有些头文件只有windows有,有些头文件只有linux才有,所以就会这样写
#ifRUN_PLATFORM==PLATFORM_WINDOWS#include#include#include#include#include#include#include#include#endif#ifRUN_PLATFORM==PLATFORM_LINUX#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#endif2.统一windows和linux的部分操作
因为很多东西在windows和linux上的类型定义是不同的,但是用法大体是一样的,所以为了方便,就用宏统一起来.
#ifRUN_PLATFORM==PLATFORM_WINDOWS#defineMY_THREADHANDLE#defineMY_SOCKETSOCKET#defineNULL_THREADNULL#defineTHREAD_CALLBACK_DECLEAR(func)staticDWORDWINAPIfunc(LPVOIDargs)#defineTHREAD_CALLBACK(class,func)DWORDWINAPIclass##::##func(LPVOIDargs)#defineCREATE_THREAD(thread,func,args)thread=CreateThread(NULL,0,func,args,0,NULL)#defineCLOSE_THREAD(thread)\if(thread!=NULL_THREAD)\{\TerminateThread(thread,0);\CloseHandle(thread);\thread=NULL_THREAD;\}#defineCLOSE_SOCKET(socket)closesocket(socket);#defineCLASS_NAME(T)string(typeid(T).name()).substr(strlen("class"))#defineSPRINTF(buffer,bufferSize,...)sprintf_s(buffer,bufferSize,__VA_ARGS__)#elifRUN_PLATFORM==PLATFORM_LINUX#defineMY_THREADpthread_t#defineMY_SOCKETunsignedint#defineNULL_THREAD0#defineSOCKADDR_INsockaddr_in#defineTHREAD_CALLBACK_DECLEAR(func)staticvoid*func(void*args)#defineTHREAD_CALLBACK(class,func)void*class##::##func(void*args)#defineCREATE_THREAD(thread,func,args)pthread_create(&thread,NULL,func,args)#defineCLOSE_THREAD(thread)\if(thread!=NULL_THREAD)\{\pthread_cancel(thread);\thread=NULL_THREAD;\}#defineCLOSE_SOCKET(socket)close(socket);#defineCLASS_NAME(T)removePreNumber(typeid(T).name())#defineSPRINTF(buffer,bufferSize,...)sprintf(buffer,__VA_ARGS__)#endif其实也就是线程,socket,sprintf的用法统一.
3.一些简单函数的宏,用于提高效率
这类宏其实主要的还是为了效率考虑,一些函数本身就是为了高效运行而存在的,如果再因为调用函数而有一些性能上的损失,真是有点太可惜了.
虽然内联可以达到同样的效果.但是是否真的内联了,还得看编译器实现,并不是定义放到头文件就是内联.我也没有去测试到底怎么样才能保证真的内联.实在是没有多余的时间花在这上面了.所以为了保证没有函数调用的开销,用宏还是最保险的.
//设置value的指定位置pos的字节的值为byte,并且不影响其他字节#defineSET_BYTE(value,b,pos)value=(value&~(0x000000FF<<(8*pos)))|(b<<(8*pos))//获得value的指定位置pos的字节的值#defineGET_BYTE(value,pos)(value&(0x000000FF>(8*pos)#defineGET_BIT(value,pos)(((value&(1>(pos))&1)#defineSET_BIT(value,pos,bit)value=value&~(1<<(pos))|((bit)=-MathUtility::MIN_DELTA&&value<=MathUtility::MIN_DELTA)#defineIS_FLOAT_EQUAL(value1,value2)(IS_FLOAT_ZERO(value1-value2))#defineIS_VECTOR3_EQUAL(vec0,vec1)(IS_FLOAT_ZERO(vec0.x-vec1.x)&&IS_FLOAT_ZERO(vec0.y-vec1.y)&&IS_FLOAT_ZERO(vec0.z-vec1.z))#defineIS_VECTOR2_EQUAL(vec0,vec1)(IS_FLOAT_ZERO(vec0.x-vec1.x)&&IS_FLOAT_ZERO(vec0.y-vec1.y))#defineLENGTH_XY(x,y)sqrt(x*xy*y)#defineLENGTH_XYZ(x,y,z)sqrt(x*xy*yz*z)#defineLENGTH_2(vec)sqrt(vec.x*vec.xvec.y*vec.y)#defineLENGTH_3(vec)sqrt(vec.x*vec.xvec.y*vec.yvec.z*vec.z)#defineSQUARED_LENGTH_XY(x,y)(x*xy*y)#defineSQUARED_LENGTH_XYZ(x,y,z)(x*xy*yz*z)#defineSQUARED_LENGTH_2(vec)(vec.x*vec.xvec.y*vec.y)#defineSQUARED_LENGTH_3(vec)(vec.x*vec.xvec.y*vec.yvec.z*vec.z)#defineLENGTH_LESS_3(vec,length)(vec.x*vec.xvec.y*vec.yvec.z*vec.z4.一些只能使用宏来实现的功能
比如获取当前行号,当然,这是自带的一个宏,可以获取当前行号,类型是整数,只是我这里使用#将其转换为了一个字符串,因为宏展开也是有顺序的,所以这里只能定义两个宏来将__LINE__转换为字符串.
#defineSTR(t)#t#defineLINE_STR(v)STR(v)#define_FILE_LINE_"File:"string(__FILE__)",Line:"LINE_STR(__LINE__)5.简单替换
这也许就是宏最简单的用法了,只是单纯的替换文本.
#defineFOR_I(count)for(uinti=0;i整数转字符串,避免使用堆内存,只使用栈内存,安全而且高效.
6.第4条和第5条的结合,目的还是简化代码
第4条中用到了#用于将宏参数转换为字符串,其他的还有##用于连接文本,__ARGS__用于获取宏参数中的可变参数列表
//用于遍历stl容器,要在循环结束后添加END#defineFOREACH(iter,stl)\autoiter=stl.begin();\autoiter##End=stl.end();\FOR(stl,;iter!=iter##End;iter)//不带内存合法检测的常规内存申请#defineNORMAL_NEW(className,ptr,...)\NULL;\ptr=newclassName(__VA_ARGS__);\if(ptr==NULL)\{\ERROR(string("cannotallocmemory!")"className:"STR(className));\}//生成静态字符串常量的名字#defineNAME(name)STR_##name//声明一个静态字符串常量#defineDECLARE_STRING(name)staticconstchar*NAME(name)//定义一个静态字符串常量#defineDEFINE_STRING(name)constchar*StringDefine::NAME(name)=STR(name)//创建一个消息对象#definePACKET(classType,packet)classType*packet=mNetServer->createPacket(packet,NAME(classType))基本也就以上这些了,总之就是用来简化代码,提高运行效率,实现一些常规函数无法实现的功能.总的来说,我还是比较喜欢宏的.
[声明]本网转载网络媒体稿件是为了传播更多的信息,此类稿件不代表本网观点,本网不承担此类稿件侵权行为的连带责任。故此,如果您发现本网站的内容侵犯了您的版权,请您的相关内容发至此邮箱【779898168@qq.com】,我们在确认后,会立即删除,保证您的版权。