使用C格式字符串构建C ++字符串的最佳方法是什么?

时间:2014-11-18 14:45:16

标签: c++ c++03

我想在我公司的代码中添加一个std::string fmtString(char const * fmt, ...);函数,它允许我以printf方式从C对象构建C ++字符串。我真的很喜欢C格式字符串的可读性,并希望将其用来代替使用operator<<operator+的C ++样式。

  1. 这是一个糟糕/低效的想法吗?
  2. 这是否已经存在,或者在没有自定义方法的情况下,C ++字符串的C风格构建是否已经很容易?
  3. 实施这种方法的最佳方法是什么?它必须是线程安全的。
  4. 编辑:这需要在没有C ++ 11的情况下工作,但我仍然对C ++ 11解决问题的方法感兴趣!

3 个答案:

答案 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来实现您的功能。

此函数的返回值将帮助您确定所需缓冲区的大小。