c/c++ 之宏的黑魔法

声明:

部分内容来源于:

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;

>>>>>
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]


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值