声明:
部分内容来源于:
https://my.oschina.net/funix/blog/375406http://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;
>>>>>
aaa
char const [4]
a
char
123
int
可以看到,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]