我使用的是Visual Studio 2008,我想实现没有变量参数列表的字符串格式化函数.
如何用pre-c 0x(VS2008)实现“Variadic Template”?
有没有任何图书馆实现这样的提升?
还是另一种方式来实现呢?
这是我的示例代码.
(当然,由于我使用的是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.Preprocessor)
>使用Cons-Lists(cons(1)(“some string”)(foo))
>使用对象并重载一些运算符(operator()),或者像运算符%(如Boost.Format))
第一个选项是有点棘手,我觉得,因为并不是每个人都可以很容易的理解宏,所以我只会保留它的短期解决方案,如果你计划迁移到C 0x很快.
第三个选项可能会提供一个很好的自定义触摸(使用许多语言的%符号进行格式化),但这也意味着需要记住这个特定的“可变”功能如何每次都工作.
>定义只涉及模板,所以它比1更可读和维护.
您可以定义一次机器,然后可以将其重新用于任何“可变”功能(并且它们仍然是功能),因此它更加一致,并且可以节省您的工作
例如,这里是如何工作的:
这个例子包括:
#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,Next>,typename cons_result<Next,U>::type> type; };
缺点模板本身,用一个魔术运算符()来附加值.请注意,它会创建一个不同类型的新项目:
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,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,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 %i\n",cons(1)); VarPrint(std::cout,"mix of %i and %s\n",cons(2)("foo")); }
integer 1 mix of 2 and foo