【问题标题】:boost::format with variadic template arguments带有可变模板参数的 boost::format
【发布时间】:2014-11-09 15:58:55
【问题描述】:

假设我有一个使用完美转发的printf-like 函数(用于记录):

template<typename... Arguments>
void awesome_printf(std::string const& fmt, Arguments&&... args)
{
    boost::format f(fmt);
    f % /* How to specify `args` here? */;
    BlackBoxLogFunction(boost::str(f).c_str());
}

(我没有编译这个,但我的真正功能遵循这个指南)

如何将可变参数“展开”到 boost::format 变量 f

【问题讨论】:

  • 我不知道它是否会起作用,但是您是否尝试过,例如args...?
  • @JoachimPileborg 我确实尝试过:coliru.stacked-crooked.com/a/9e651d5f7532cc67,不幸的是它不起作用(除非我做错了)。
  • 这就是扩展可变参数模板参数的方式。不幸的是,Boost 格式使用重载的% 运算符来分隔参数,这不适用于扩展的参数包。
  • @JoachimPileborg 嗯,它是在模板参数包和完美转发存在之前设计的,这占了设计的很大一部分(以及它的弱点)。
  • 我想可以使用 C++1z 附带的折叠运算符来完成。 en.cppreference.com/w/cpp/language/fold 。相关:stackoverflow.com/questions/27582862/…

标签: c++ c++11 boost variadic-templates boost-format


【解决方案1】:

与可变参数模板一样,您可以使用递归:

std::string awesome_printf_helper(boost::format& f){
    return boost::str(f);
}

template<class T, class... Args>
std::string awesome_printf_helper(boost::format& f, T&& t, Args&&... args){
    return awesome_printf_helper(f % std::forward<T>(t), std::forward<Args>(args)...);
}

template<typename... Arguments>
void awesome_printf(std::string const& fmt, Arguments&&... args)
{
    boost::format f(fmt);

    auto result = awesome_printf_helper(f, std::forward<Arguments>(args)...);

    // call BlackBoxLogFunction with result as appropriate, e.g.
    std::cout << result;
}

Demo.


在 C++17 中,只需 (f % ... % std::forward&lt;Arguments&gt;(args)); 即可。

【讨论】:

  • 在输出的末尾强制换行对于可重用性来说不是一个好主意:它很容易集成到格式字符串中,并且强制排除了 before 行的扩展它结束了。
【解决方案2】:

我做了一些谷歌搜索,发现了一个有趣的解决方案:

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

template<typename... Arguments>
void format_vargs(std::string const& fmt, Arguments&&... args)
{
    boost::format f(fmt);
    int unroll[] {0, (f % std::forward<Arguments>(args), 0)...};
    static_cast<void>(unroll);

    std::cout << boost::str(f);
}

int main()
{
    format_vargs("%s %d %d", "Test", 1, 2);
}

我不知道这是否是推荐的解决方案,但它似乎有效。我不喜欢 hacky 的 static_cast 用法,这似乎有必要让 GCC 上未使用的变量警告静音。

【讨论】:

  • 你可以通过不创建临时变量using unroll = int[]; unroll{0, (f % std::forward&lt;Arguments&gt;(args), 0)...};来避免强制转换@
  • @Praetorian 效果很好;但是using 似乎是多余的...我想知道为什么int[] { /* ... */ }; 不起作用...
  • 因为语法不允许。要么是using,要么是演员。
  • 请注意,这个技巧依赖于f % x % y; 等同于f % x; f % y;。 (另外,我会将f % std::forward&lt;Arguments&gt;(args) 转换为void,以防万一类型具有重载的逗号运算符。)
  • 您也可以使用std::initializer_list&lt;int&gt;{(f% forward&lt;Args&gt;(args), 0)...}; 来避免强制转换(以及没有参数的情况)(但需要包含)。
【解决方案3】:

只是总结一下void.pointer's solution和建议的提示by PraetorianT.C.Jarod42,让我提供最终版本(online demo

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

template<typename... Arguments>
std::string FormatArgs(const std::string& fmt, const Arguments&... args)
{
    boost::format f(fmt);
    std::initializer_list<char> {(static_cast<void>(
        f % args
    ), char{}) ...};

    return boost::str(f);
}

int main()
{
    std::cout << FormatArgs("no args\n"); // "no args"
    std::cout << FormatArgs("%s; %s; %s;\n", 123, 4.3, "foo"); // 123; 4.3; foo;
    std::cout << FormatArgs("%2% %1% %2%\n", 1, 12); // 12 1 12
}

另外,as it was noted by T.C.,使用自 C++17 起可用的 fold expression 语法,可以以更简洁的方式重写 FormatArgs 函数

template<typename... Arguments>
std::string FormatArgs(const std::string& fmt, const Arguments&... args)
{
    return boost::str((boost::format(fmt) % ... % args));
}

【讨论】:

  • 哇,我喜欢折叠表达式...我什至不知道他们添加了那个。惊人的。为清楚起见的问题:折叠表达式解决方案中是否需要内括号?换句话说,这行得通吗:boost::str(boost::format(fmt) % ... % args);
  • 我扩展了您的演示以包含您的折叠表达式解决方案:coliru.stacked-crooked.com/a/bfabe441fba08d62 我还尝试删除那些内括号,但它无法编译。不过,知道为什么会很有教育意义。诊断没有多大帮助。
  • @void.pointer,折叠表达式周围的括号似乎是它的强制性部分,不能省略。您可以查看grammar description of fold expression(“说明”部分)。当折叠表达式展开时,它周围的括号被删除,所以在 boost::str((fold expr)) 的情况下,我们需要有额外的括号才能在折叠表达式展开后得到 boost::str(expanded fold expr)
猜你喜欢
  • 2014-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-20
  • 2015-12-14
  • 2015-05-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多