c/c++ 之宏的黑魔法

来源:互联网 发布:雕刻软件有哪些 编辑:程序博客网 时间:2024/06/10 07:42

声明:

部分内容来源于:

https://my.oschina.net/funix/blog/375406

http://blog.csdn.net/morewindows/article/details/6697488


一、基础用法

举两个最常用的例子:
#define MAX_LINE 1024#define MULT(x,y) x*y

看到这里大部分人应该都知道,第二种用法是错的,来看个例子:
std::cout << MULT(1+2,3) << std::endl;>>>>>7

这是为什么呢,我们来看看MULT宏展开之后的样子(TOSTRING宏下面会介绍):
std::cout << TOSTRING(MULT(1+2,3)) << std::endl;>>>>>1+2*3

很显然,结果是错的,因为宏做的工作仅仅只是文本替换
MULT宏展开后,x和y不是我们想象的那样是两个独立的变量,而是变成了三个,且运算顺序也和预期的相反
所以我们需要修改MULT,使x和y变成我们想要的样子,如下:
#define MULT(x,y) ((x)*(y))std::cout << MULT(1+2,3) << std::endl;std::cout << TOSTRING(MULT(1+2,3)) << std::endl;>>>>>9((1+2)*(3))

里面的括号保证了运算顺序,外面的括号可以防止被其他运算干扰
从这里可以看出,如果我们不小心,方便的宏函数很可能造成无法预料的结果或错误,而且还很难调试

二、类的设计

class tbase{public:virtual ~tbase(){};virtual void setID(int id = 0) = 0;virtual int getID() = 0;};class tson : tbase{public:tson(){};~tson(){};virtual void setID(int id = 0);virtual int getID();private:int m_iID;};


如果想要修改setID这个接口参数的默认值,我们需要同时修改tbase和tson,假如有很多继承下来的子类,这会是一件很麻烦的事,而且遗漏了编译器也不会警告,甚至你可能永远也不会发现
现在我们用宏来实现同样的设计:
#define INTERFACE_TBASE(end)\virtual void setID(int id = 0)##end\virtual int getID()##end#define DEFINE_TBASE INTERFACE_TBASE(=0;)#define DEFINE_TSON INTERFACE_TBASE(;)class tbase{public:virtual ~tbase(){};public:DEFINE_TBASE;};class tson : tbase{public:tson(){};~tson(){};public:DEFINE_TSON;private:int m_iID;};

这样只需要修改INTERFACE_TBASE即可

三、#、#@、##

#define _TOSTRING(s) #s #define TOSTRING(s) _TOSTRING(s)#define _TOSTRING2(s) #@s#define TOSTRING2(s) _TOSTRING2(s)#define _MACROCAT2(x, y) x##y#define MACROCAT2(x, y) _MACROCAT2(x, y)#define MACROCAT3(x, y, z) MACROCAT2(MACROCAT2(x, y), z)

其中:
#:将参数转化为字符串,即加个“”(双引号)
#@:将参数转化为单字符,即加个‘’(单引号)
##:拼接标识符
直接上例子:
cout << TOSTRING(aaa) << endl;cout << typeid(TOSTRING(aaa)).name() << endl;cout << TOCHAR(a) << endl;cout << typeid(TOCHAR(a)).name() << endl;int abc = 123;cout << MACROCAT3(a, b, c) << endl;cout << typeid(MACROCAT3(a, b, c)).name() << endl;>>>>>aaachar const [4]achar123int

可以看到,TOSTRING将aaa转化成了const char[4],TOCHAR将a转化成了char,MACROCAT3帮我们生成了abc这个变量的标识符

四、应用-日志

#define _TOSTRING(s) #s #define TOSTRING(s) _TOSTRING(s)#define _TOCHAR(s) #@s#define TOCHAR(s) _TOCHAR(s)#define _MACROCAT2(x, y) x##y#define MACROCAT2(x, y) _MACROCAT2(x, y)#define MACROCAT3(x, y, z) MACROCAT2(MACROCAT2(x, y), z)#define PP_RSEQ_N() \63, 62, 61, 60, \59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \9, 8, 7, 6, 5, 4, 3, 2, 1, 0#define PP_ARG_N( \_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \_31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \_51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \_61, _62, _63, N, ...) N#if defined(WIN32) || defined(WIN64)#define BRACKET_L() (#define BRACKET_R() )#define PP_NARG_(...) PP_ARG_N BRACKET_L() __VA_ARGS__ BRACKET_R()#define PP_NARG(...) PP_NARG_ ( __VA_ARGS__, PP_RSEQ_N() )#else#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)#define PP_NARG(...) PP_NARG_(__VA_ARGS__, PP_RSEQ_N())#endif//获取时间std::string getTimeStr(){struct tm t;time_t now;time(&now);localtime_s(&t, &now);char tmp[64];sprintf_s(tmp, sizeof(tmp), "%02d:%02d:%02d", t.tm_hour,t.tm_min,t.tm_sec);return tmp;}//自动日志#define AUTOLOG autoLog _auto(__FUNCTION__)//流日志#define LOG(os)cout<<"["<<getTimeStr().c_str()<<"]["<<__FUNCTION__<<"."<<__LINE__<<"]"<<os<<endl;//格式日志#define LOGP(format,...)\printf(MACROCAT3("[%s][%s.%d]{%s.%d}",format,"\n"),\getTimeStr().c_str(),\__FUNCTION__,\__LINE__,\format,\PP_NARG(__VA_ARGS__),\__VA_ARGS__);class autoLog{public:autoLog(const char* func){sprintf_s(m_strFunc, func);std::cout << std::endl;std::cout << "-----------[" << m_strFunc << "][begin]" << std::endl;};~autoLog(){std::cout << "+++++++++++[" << m_strFunc << "][end]" << std::endl;};private:char m_strFunc[20];};

测试结果:
AUTOLOG;LOG(TOSTRING(hello));LOGP(TOSTRING(%s),TOSTRING(hello));LOGP(TOSTRING("%s,%s!"), TOSTRING(hello),TOSTRING(world));//↑这里使用了逗号,所以加了个双引号,暂时没有好办法,会导致输出多对双引号>>>>>-----------[main][begin][20:47:37][main.64]hello[20:47:37][main.65]{%s.1}hello[20:47:37][main.66]{"%s,%s!".2}"hello,world!"+++++++++++[main][end]


原创粉丝点击