用C++可变参数模板实现类似C语言的printf

在以前,C++要实现可变参数的函数,得用C语言的va_list那一套东西,直到C++11的可变参数模板诞生,正好最近在写一个日志模块,是时候了解一下可变参数模板了。

下面这个简单的Log函数,只使用##进行占位,并没有区分类型,有点像C#的{0}、{1}……

#include 
#include 
#include 

template
void mylog(const std::string& format, const Args&... args) {
	std::string temp(format);
	mylog(temp, args...);
}

template
void mylog(std::string& format, const Next& next, const Other&... other) {
	auto index = format.find_first_of("##");
	if (index == std::string::npos) {
		mylog(format);
	}
	else {
		std::ostringstream oss;
		oss << next;
		format.replace(index, 2, oss.str());
		mylog(format, other...);
	}
}

void mylog(std::string& format) {
	std::cout << format;
}

int main() {
	// 无占位符
	mylog("Hello Indian Mi fans!\n");

	// 有占位符
	mylog("Do you like Mi##?\n", "4i");

	// 占位符超过参数数量,多余的占位符原样输出
	mylog("My name is ####!\n", "Stephen");

	// 参数数量超过占位符数量,多余的参数被忽略
	mylog("## is ## years old, ## meters tall.\n", "Stephen", 18, 1.75f, "useless", "useless");

	// 参数不能被格式化为字符串,发生编译错误
	//mylog("Here comes a compile error ##\n", std::cout);

	return 0;
}

运行结果:


可以看到,一共有三个版本的mylog函数,前两个都是模板函数,第一个函数用来将const的格式化字符串简单地转换为非const形式的调用,第二个函数则是核心,用某种规则进行递归,第三个函数则是递归出口。

要注意的是:

1. Args...可以是任意数量个参数,包括0个。

2. args...形式的参数包只能通过递归的方式解开。

3. 用sizeof...(args)可以得到此参数包的参数个数。

4. 宏替换有效:#define DEBUG(format, ...) mylog(format, ##__VA_ARGS__)。

你可能感兴趣的:(C/C++,C++,C语言,模板,C++11)