在C++中有没有更优雅的方式将sprintf和std :: string结合起来?

Yur*_*nko 4 c++ string printf string-formatting c++11

在我的C++代码中经常使用以下类型的辅助函数:

static inline std::string stringf(const char *fmt, ...)
{
    std::string ret;
    // Deal with varargs
    va_list args;
    va_start(args, fmt);
    // Resize our string based on the arguments
    ret.resize(vsnprintf(0, 0, fmt, args));
    // End the varargs and restart because vsnprintf mucked up our args
    va_end(args);
    va_start(args, fmt);
    // Fill the string
    if(!ret.empty())
    {
        vsnprintf(&ret.front(), ret.size() + 1, fmt, args);
    }
    // End of variadic section
    va_end(args);
    // Return the string
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

它有一些上升空间:

  1. 字符串长度没有任意限制
  2. 字符串是就地生成的,不会被复制(如果RVO正常工作)
  3. 外面没有惊喜

现在,我有一些问题:

  1. 重新扫描varargs有点难看
  2. 事实上,std :: string在内部是一个连续的字符串,后面直接有一个空终止符的空格,似乎没有在规范中明确说明.通过以下事实暗示 - > c_str()必须是O(1)并返回以null结尾的字符串,我相信&(data()[0])应该等于&(*begin())
  3. vsnprintf()被调用两次,可能是第一次进行昂贵的丢弃工作

有人知道更好的方法吗?

Nir*_*man 5

你结婚了(双关语)std::sprintf()吗?如果你正在使用C++和现代的哦std::string,为什么不充分利用新的语言功能并使用可变参数模板来做一个类型安全的sprintf返回std::string

看看这个非常漂亮的实现:https://github.com/c42f/tinyformat.我认为这解决了你所有的问题.


bor*_*sbn 5

当您添加标签时,c++11我想您可以使用它。然后您可以将代码简化为:

namespace fmt {

template< class ...Args >
std::string sprintf( const char * f, Args && ...args ) {
    int size = snprintf( nullptr, 0, f, args... );
    std::string res;
    res.resize( size );
    snprintf( & res[ 0 ], size + 1, f, args... );
    return res;
}

}

int main() {
    cout << fmt::sprintf( "%s %d %.1f\n", "Hello", 42, 33.22 );
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

http://ideone.com/kSnXKj