我正在使用Visual Studio 2008,我想实现字符串格式化功能没有变量参数列表。
如何实现"可变模板"与pre-c++0x(VS2008)?
有没有像boost这样的库实现这个?
或者另一种实现方法?
这是我的示例代码。(当然,这不能被编译,因为我使用VS2008。)
bool VarPrint(std::ostringstream& out, const std::string& s)
{
std::string::size_type offset = 0;
if((offset = s.find("%")) != std::string::npos)
{
if(!(offset != s.size() - 1 && s[offset + 1] == '%'))
{
ASSERT(!"Missing Arguments!");
return false;
}
}
out << s;
return true;
}
template<typename T, typename... Args>
bool VarPrint(std::ostringstream& out, const std::string& s, const T& value, const Args&... args)
{
std::string::size_type prev_offset = 0;
std::string::size_type curr_offset = 0;
while((curr_offset = s.find("%", prev_offset)) != std::string::npos)
{
out << s.substr(prev_offset, curr_offset);
if(!(curr_offset != s.size() - 1 && s[curr_offset + 1] == '%'))
{
out << value;
if(curr_offset + 2 < s.length())
return VarPrint(out, s.substr(curr_offset + 2), args...); return true;
}
prev_offset = curr_offset + 2;
if(prev_offset >= s.length)
break;
}
ASSERT(!"Extra Argument Provided!");
return false;
}
在c++ 03中,您有不同的可能性:
- 为0-N个参数生成重载(使用Boost)。预处理器(例如)
- 使用控制列表(
cons(1)("some string")(foo)
) - 使用对象和重载一些操作符(
operator()
为例,或operator%
如Boost.Format)
第一个选项有点棘手,我觉得,因为不是每个人都能很容易地理解宏,所以如果你打算很快迁移到c++ 0x,我只会为短期解决方案保留它。
第三个选项可能提供一个不错的自定义触摸(格式化在许多语言中是用%
符号完成的),但这也意味着需要记住这个特定的"可变"函数每次是如何工作的。
我的个人偏好是cons
方法,因为它解决了这两个问题:
- 这个定义只涉及模板,所以它比1更具可读性和可维护性。
- 你只定义一次cons-machinery,然后你可以在任何"可变的"函数中重用它(它们仍然是函数),所以它更一致,并节省你的工作
例如,它是这样工作的:
本例将使用的include:
#include <cassert>
#include <iostream>
#include <string>
用于附加值的结果类型的帮助器(使用前置可能更有效,但这意味着以反顺序传递参数,这是违反直觉的):
template <typename T, typename Next> struct Cons;
struct ConsEmpty;
template <typename Cons, typename U>
struct cons_result;
template <typename U>
struct cons_result<ConsEmpty, U> {
typedef Cons<U, ConsEmpty> type;
};
template <typename T, typename U>
struct cons_result<Cons<T, ConsEmpty>, U> {
typedef Cons<T, Cons<U, ConsEmpty> > type;
};
template <typename T, typename Next, typename U>
struct cons_result<Cons<T, Next>, U> {
typedef Cons<T, typename cons_result<Next, U>::type> type;
};
Cons
模板本身,带有一个神奇的operator()
附加值。注意,它创建了一个不同类型的新项目:
template <typename T, typename Next>
struct Cons {
Cons(T t, Next n): value(t), next(n) {}
T value;
Next next;
template <typename U>
typename cons_result<Cons, U>::type operator()(U u) {
typedef typename cons_result<Cons, U>::type Result;
return Result(value, next(u));
}
};
struct ConsEmpty {
template <typename U>
Cons<U, ConsEmpty> operator()(U u) {
return Cons<U, ConsEmpty>(u, ConsEmpty());
}
};
template <typename T>
Cons<T, ConsEmpty> cons(T t) {
return Cons<T, ConsEmpty>(t, ConsEmpty());
}
带着它重访VarPrint
:
bool VarPrint(std::ostream& out, const std::string& s, ConsEmpty) {
std::string::size_type offset = 0;
if((offset = s.find("%")) != std::string::npos) {
if(offset == s.size() - 1 || s[offset + 1] != '%') {
assert(0 && "Missing Arguments!");
return false;
}
}
out << s;
return true;
}
template<typename T, typename Next>
bool VarPrint(std::ostream& out,
std::string const& s,
Cons<T, Next> const& cons)
{
std::string::size_type prev_offset = 0, curr_offset = 0;
while((curr_offset = s.find("%", prev_offset)) != std::string::npos) {
out << s.substr(prev_offset, curr_offset);
if(curr_offset == s.size() - 1 || s[curr_offset + 1] != '%') {
out << cons.value;
if(curr_offset + 2 < s.length())
return VarPrint(out, s.substr(curr_offset + 2), cons.next);
return true;
}
prev_offset = curr_offset + 2;
if(prev_offset >= s.length())
break;
}
assert(0 && "Extra Argument Provided!");
return false;
}
和演示:
int main() {
VarPrint(std::cout, "integer %in", cons(1));
VarPrint(std::cout, "mix of %i and %sn", cons(2)("foo"));
}
输出:integer 1
mix of 2 and foo
在c++ 03中没有可变模板功能。Boost和其他设计良好的库以不同的方式解决了这个问题。对于函数,可以有N + 1个重载,每个重载从0到N个参数。对于类,一个定义可以包含多达N个参数,这些参数默认为某些无效类型。这个更高的限制通常可以通过一些宏来配置;因为将其设置为高会增加编译时间的开销,而将其设置为低会导致用户无法传递足够的参数。
对于您的特殊情况,我将以递归的方式实现VarPrint
。递归中的每一步都将处理一个参数,并使用修改后的格式字符串发出递归调用,并将所有左值向左移动一个位置。