我想在我公司的代码中添加一个std::string fmtString(char const * fmt, ...);
函数,它允许我以printf方式从C对象构建C ++字符串。我真的很喜欢C格式字符串的可读性,并希望将其用来代替使用operator<<
或operator+
的C ++样式。
编辑:这需要在没有C ++ 11的情况下工作,但我仍然对C ++ 11解决问题的方法感兴趣!
答案 0 :(得分:2)
template<class...Args>
std::string fmt( string_view, Args&&... )
是这种功能的C ++ 11签名。这允许运行时类型安全和受控故障。
不使用C风格的VA-args,因为这会导致任何类型的明智失败(类型检查,arg计数检查等)。
解析说string_view
在运行时需要时间,但除此之外没有根本的低效率。将一串字符转换为解析格式化程序的constexpr
解析器可能是一个好主意,可能是字符串文字。
boost
有许多C风格的C ++格式化程序。
您想要遍历格式字符串,查找格式命令。随身携带转储非格式文本。在每个format命令中,打开它并将一个或多个Args
消耗到您期望的类型的生成错误的消费者中。
%d
消费者的一个例子是:
template<class T> struct simple_printer;
template<> struct simple_printer {
void operator()( std::string& s, int x ) const {
// TODOL print x to s
}
};
template<class X, class... Ts>
std::false_type
simple_print(std::false_type, Ts&&...) { return {}; }
template<class X, class...Args>
std::true_type
simple_print(std::true_type, std::string& out, Args&&... in ) {
simple_printer<X>{}( out, std::forward<T>(t) );
}
然后在解析代码中,当你遇到%d
时,你会这样做:
if (fmt_code == "%d") {
auto worked = simple_print( std::is_convertible<Arg0, int>{}, out, std::forward<Arg0>(arg0) );
if (!worked) {
// report error then
return;
}
}
解析每个格式化命令后,使用较少的参数和格式字符串的尾端递归打印函数。
处理更复杂的格式(如%.*f
)自然更棘手。如果您请求&#34;格式化捆绑包&#34;它可能会有所帮助参数以某种方式分组在Args
中。
答案 1 :(得分:1)
这打破了类型安全性,这是从一开始就从C转向C ++的主要好处之一。如果发送错误的数据,则会出现运行时崩溃或意外输出,而不是舒适的编译失败。
尝试Boost.Format,这会做你正在做的事情,但更好:
格式库提供了一个类,用于根据格式字符串格式化参数,printf也是如此,但有两个主要区别:
- format将参数发送到内部流,因此完全是类型安全的,并且自然支持所有用户定义的类型。
- 省略号(...)无法在强类型格式的上下文中正确使用,因此带有任意参数的函数调用将被连续调用参数 取代操作者%强>
It's thread-safe if you follow the one-instance-per-thread model
你可以通过交换可变参数模板的变量来改进自己的方法,但为什么要重新发明轮子?
答案 2 :(得分:0)
不判断这是不是一个好主意,您可以使用vsnprintf
来实现您的功能。
此函数的返回值将帮助您确定所需缓冲区的大小。