编写c ++函数format_string用于格式化std :: string的sprintf

zah*_*pov 6 c++ string string-formatting

为了方便使用,我想编写类似于sprintf的格式化函数,只需返回std :: string,如下所示:

std::string format_string(const char* format, ...)
Run Code Online (Sandbox Code Playgroud)

我可以vsnprintf在那里使用但有问题 - 我事先不知道临时缓冲区应该有多长.在微软有功能_vscprintf可以做到但我觉得它不便携?

一个选项是让临时缓冲区启动一些已知的大小,然后增加它,如果看到它不够vsnprintf.有更好的方法吗?谢谢


PS请在没有Boost的情况下给出答案.我知道Boost,但我很好奇如何实现它.

Ton*_*roy 5

一种选择是让临时缓冲区启动一些已知大小,然后在使用vsnprintf. 有没有更好的办法?谢谢

您可以使用vasprintf(),但这会进行不必要的堆分配 - 平均来说不太可能更快。使用alloca你可以避免堆。或者,您可以直接写入string返回的内容:NRVO 应避免复制,而从 C++11 开始,移动语义会将成本 sans-NRVO 限制为几次指针交换。

#include <cstdio>
#include <cstdarg>
#include <alloca.h>

#include <string>
#include <iostream>

std::string stringf(const char* format, ...)
{
    va_list arg_list;                                                           
    va_start(arg_list, format);                                                 

    // SUSv2 version doesn't work for buf NULL/size 0, so try printing
    // into a small buffer that avoids the double-rendering and alloca path too...
    char short_buf[256];                                                        
    const size_t needed = vsnprintf(short_buf, sizeof short_buf,
                                    format, arg_list) + 1;
    if (needed <= sizeof short_buf)
        return short_buf;

    // need more space...

    // OPTION 1
    std::string result(needed, ' ');
    vsnprintf(result.data(), needed, format, arg_list);
    return result;  // RVO ensures this is cheap
 OR
    // OPTION 2
    char* p = static_cast<char*>(alloca(needed)); // on stack
    vsnprintf(p, needed, format, arg_list);
    return p;  // text copied into returned string
}

int main()                                                                      
{                                                                               
    std::string s = stringf("test '%s', n %8.2f\n", "hello world", 3.14);       
    std::cout << s;                                                             
}
Run Code Online (Sandbox Code Playgroud)

一个更简单且最初更快的选项是:

    std::string result(255, ' ');  // 255 spaces + NUL
    const size_t needed = vsnprintf(result.data(), result.size() + 1,
                                    format, arg_list);
    result.resize(needed); // may truncate, leave or extend...
    if (needed > 255) // needed doesn't count NUL
        vsnprintf(result.data(), needed + 1, format, arg_list);
    return result;
Run Code Online (Sandbox Code Playgroud)

潜在的问题是您分配了至少 256 个字符,但实际存储的文本很短:这可能会累加并降低与内存/缓存相关的性能。您也许可以使用 [ shrink_to_fit] http://en.cppreference.com/w/cpp/string/basic_string/shrink_to_fit)解决该问题,但标准并不要求它实际执行任何操作(要求是“无约束力”)。如果您最终不得不复制到一个新的精确大小的字符串,您不妨使用本地字符数组。