Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何创建可变模板字符串格式化程序_C++_Templates_C++11_String Formatting_Variadic Templates - Fatal编程技术网

C++ 如何创建可变模板字符串格式化程序

C++ 如何创建可变模板字符串格式化程序,c++,templates,c++11,string-formatting,variadic-templates,C++,Templates,C++11,String Formatting,Variadic Templates,我们需要一直格式化字符串。如果能够说: std::string formattedStr = format("%s_%06d.dat", "myfile", 18); // myfile_000018.dat 有没有C++的方法?我考虑了一些备选方案: snprintf:使用原始char缓冲区。在现代C++代码中不好。 std::stringstream:不支持格式模式字符串,您必须将笨拙的iomanip对象推入流中 boost::format:使用特设运算符重载%来指定参数。丑陋的 现在

我们需要一直格式化字符串。如果能够说:

std::string formattedStr = format("%s_%06d.dat", "myfile", 18); // myfile_000018.dat
有没有C++的方法?我考虑了一些备选方案:

  • snprintf
    :使用原始
    char
    缓冲区。在现代C++代码中不好。
  • std::stringstream
    :不支持格式模式字符串,您必须将笨拙的iomanip对象推入流中
  • boost::format
    :使用特设运算符重载
    %
    来指定参数。丑陋的

现在我们有了C++11,难道没有更好的方法来使用可变模板吗?

当然可以用C++11编写可变模板。最好是把已经存在的东西包装起来,而不是自己写整个东西。如果您已经在使用Boost,那么将
Boost::format
包装成这样非常简单:

#include <boost/format.hpp>
#include <string>

namespace details
{
    boost::format& formatImpl(boost::format& f)
    {
        return f;
    }

    template <typename Head, typename... Tail>
    boost::format& formatImpl(
        boost::format& f,
        Head const& head,
        Tail&&... tail)
    {
        return formatImpl(f % head, std::forward<Tail>(tail)...);
    }
}

template <typename... Args>
std::string format(
        std::string formatString,
        Args&&... args)
{
    boost::format f(std::move(formatString));
    return details::formatImpl(f, std::forward<Args>(args)...).str();
}
如果您不想使用Boost(但确实应该),那么您也可以包装
snprintf
。因为我们需要管理char缓冲区和旧式的非类型安全的可变长度参数列表,所以它更复杂,更容易出错。通过使用
unique\ptr
,它变得更干净了一些:

#include <cstdio> // snprintf
#include <string>
#include <stdexcept> // runtime_error
#include <memory> // unique_ptr

namespace details
{
    template <typename... Args>
    std::unique_ptr<char[]> formatImplS(
            size_t bufSizeGuess,
            char const* formatCStr,
            Args&&... args)
    {
        std::unique_ptr<char[]> buf(new char[bufSizeGuess]);

        size_t expandedStrLen = std::snprintf(buf.get(), bufSizeGuess, formatCStr, args...);

        if (expandedStrLen >= 0 && expandedStrLen < bufSizeGuess)
        {
            return buf;
        } else if (expandedStrLen >= 0
                   && expandedStrLen < std::numeric_limits<size_t>::max())
        {
            // buffer was too small, redo with the correct size
            return formatImplS(expandedStrLen+1, formatCStr, std::forward<Args>(args)...);
        } else {
            throw std::runtime_error("snprintf failed with return value: "+std::to_string(expandedStrLen));
        }
    }

    char const* ifStringThenConvertToCharBuf(std::string const& cpp)
    {
        return cpp.c_str();
    }

    template <typename T>
    T ifStringThenConvertToCharBuf(T const& t)
    {
        return t;
    }
}

template <typename... Args>
std::string formatS(std::string const& formatString, Args&&... args)
{
    // unique_ptr<char[]> calls delete[] on destruction
    std::unique_ptr<char[]> chars = details::formatImplS(4096, formatString.c_str(),
                details::ifStringThenConvertToCharBuf(args)...);

    // string constructor copies the data
    return std::string(chars.get());
}
#包括//snprintf
#包括
#包括//runtime\u错误
#包括//unique\u ptr
命名空间详细信息
{
模板
std::唯一的\u ptr formatImplS(
尺寸,
字符常量*formatCStr,
Args&&…Args)
{
std::unique_ptr buf(新字符[bufSizeGuess]);
size_t expandedStrLen=std::snprintf(buf.get(),bufSizeGuess,formatCStr,args…);
如果(expandedStrLen>=0&&expandedStrLen=0,则为else
&&expandedStrLen
就格式规范而言,
snprintf
boost::format
之间存在一些差异,但您的示例可以同时使用这两种格式。

使用可变模板实现字符串格式。例如:

// printf syntax:
std::string formattedStr = fmt::sprintf("%s_%06d.dat", "myfile", 18);

// Python-like syntax:
std::string formattedStr = fmt::format("{}_{:06}.dat", "myfile", 18);

免责声明:我是该库的作者。

您的所有代码都丢失了
std::forward
std::move
,并对
formatString
进行了不必要的复制,作为替代(而不是更正)。有一个技巧可以压缩所有代码并完全避免递归,但是clang抱怨未使用的局部变量:我把它取回来,boost格式需要一个
常量字符串&
,所以不太清楚是否有不必要的副本。我修改了我的版本,显然没有复制:@isarandi“它当然可以用C++11编写,带有可变模板”。你能提供一个关于C++ 11解决方案的答案吗?@ dofx,嗯,这就是答案本身:)rasLo,它似乎不能自动产生一个STD::String结果。此外,所需的语法太奇怪,无法在整个项目中使用。
// printf syntax:
std::string formattedStr = fmt::sprintf("%s_%06d.dat", "myfile", 18);

// Python-like syntax:
std::string formattedStr = fmt::format("{}_{:06}.dat", "myfile", 18);