如何创建可变参数模板字符串格式化程序 [英] How to create a variadic template string formatter

查看:79
本文介绍了如何创建可变参数模板字符串格式化程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们需要一直格式化字符串。可以这么说:

We need to format strings all the time. It would be so nice to be able to say:

std::string formattedStr = format("%s_%06d.dat", "myfile", 18); // myfile_000018.dat

是否有C ++方式?我考虑过的一些替代方法:

Is there a C++ way of doing this? Some alternatives I considered:


  • snprintf :使用原始的 char 缓冲区。在现代C ++代码中不是很好。

  • std :: stringstream :不支持格式模式字符串,相反,您必须推送笨拙的iomanip对象

  • boost :: format :使用%的临时​​运算符重载指定参数。丑陋的。

  • snprintf: uses raw char buffers. Not nice in modern C++ code.
  • std::stringstream: does not support format pattern strings, instead you must push clumsy iomanip objects into the stream.
  • boost::format: uses an ad-hoc operator overload of % to specify the arguments. Ugly.

可变参数模板现在没有更好的方法了,因为我们有了C ++ 11?

Isn't there a better way with variadic templates now that we have C++11?

推荐答案

它肯定可以用C ++ 11和可变参数模板编写。最好包装已经存在的东西,而不是尝试自己编写整个东西。如果您已经在使用Boost,则包装 boost :: format 这样非常简单:

It can certainly be written in C++11 with variadic templates. It's best to wrap something that already exists than to try to write the whole thing yourself. If you are already using Boost, it's quite simple to wrap boost::format like this:

#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();
}

您可以按照自己的方式使用:

You can use this the way you wanted:

std::string formattedStr = format("%s_%06d.dat", "myfile", 18); // myfile_000018.dat

如果您不想使用Boost(但您确实应该),那么您还可以包装 snprintf 。由于我们需要管理char缓冲区和旧式的非类型安全的可变长度参数列表,因此它涉及更多并且容易出错。通过使用 unique_ptr 可以使它更干净:

If you don't want to use Boost (but you really should) then you can also wrap snprintf. It is a bit more involved and error-prone, since we need to manage char buffers and the old style non-type-safe variable length argument list. It gets a bit cleaner by using unique_ptr's:

#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 boost :: format 就格式规范而言,但您的示例可同时使用。

There are some differences between snprintf and boost::format in terms of format specification but your example works with both.

这篇关于如何创建可变参数模板字符串格式化程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆