是否可以从模板参数包中构建“过滤后的元组”或“过滤参数包”?

时间:2013-12-07 08:48:37

标签: c++ templates variadic-templates

我有一种情况,我想在模板参数包中的每个值上调用一些函数,并将调用该函数的结果存储在堆栈分配的变量中。例如:

#include <string>
#include <utility>
#include <functional>

char const* format_value(double x) { /* ... */ }
std::string format_value(long x) { /* ... */ }

template <typename Sink, typename... Values>
Sink& format(Sink& target, Values... values)
{
    // Does not compile; Not valid C++11
    typedef std::tuple<typename std::result_of<format_value(values)>::type...> tuple_type;
    tuple_type slices(format_value(values)...);
    /* Code that does things with the results. */
}

是否可以进行此类过滤,如果可以,我该怎么做?

2 个答案:

答案 0 :(得分:1)

好的。我看到your code at github并希望知道你想要达到的目的。

我认为您仍然可以避免对format_buffer()的递归调用。这是我正在谈论的部分:

template <typename Slice, typename... Slices>
char const* format_buffer(char* ptr, std::size_t length, Slice const& slice, Slices const& ...slices)
{
    std::size_t const size = slice.size();
    std::copy_n(slice.data(), size, ptr);
    return format_buffer(ptr + size, length - size, slices...);
}

template <typename Sink, typename... Slices>
Sink& write_impl(Sink& target, Slices &&...slices)
{
    std::size_t const length = sum_sizes(slices...);
    OptimisticBuffer<256> buff(length);
    char* ptr = buff.GetAs<char>();
    char const* endPtr = format_buffer(ptr, length, slices...);
    target.append(ptr, endPtr - ptr);
    return target;
}

可以替换为:

struct unpack { template<typename ...T> unpack(T && ...) {} };

template <typename Sink, typename... Slices>
Sink& write_impl(Sink& target, Slices &&...slices)
{
    std::size_t const length = sum_sizes(slices...);
    OptimisticBuffer<256> buff(length);
    char* ptr = buff.GetAs<char>();
    char *origin = ptr;
    unpack { (ptr = std::copy_n(slices.data(), slices.size(), ptr)) ... } ;
    target.append(origin, ptr - origin);
    return target;
}

魔法发生在unpack {}行。对于slice中的每个slices,您调用std::copy_n,返回ptr+size(然后存储在ptr中),这将成为下次调用{的输入{1}}等等:

std::copy_n

扩展到:

 unpack { (ptr = std::copy_n(slices.data(), slices.size(), ptr)) ... } ;

请注意,由于它使用列表初始化,因此表达式评估的顺序保证是从左到右,即自上而下扩展形式!

由于GCC has bug since 4.7.0(这就是为什么它当前不起作用),你可以这样写:

 unpack 
 { 
     (ptr = std::copy_n(slices0.data(), slices0.size(), ptr)),
     (ptr = std::copy_n(slices1.data(), slices1.size(), ptr)),
     (ptr = std::copy_n(slices2.data(), slices2.size(), ptr)),
      .
      .
     (ptr = std::copy_n(slicesN.data(), slicesN.size(), ptr)),
 };

另一个改进是:我会实现using unpack = void const*[]; unpack {(ptr=std::copy_n(slicesN.data(),slicesN.size(), ptr))...};` 而不是sum_sizes(),而不是这个:

sum

我可以这样写:

 std::size_t const length = sum_sizes(slices...);

通过这种方式, std::size_t const length = sum(slices.size()...); 可以更加重复使用,例如:

sum()

如果你需要这些。好吧,如果是 std::size_t const x = sum(args.get_element_size()...); std::size_t const y = sum(sizeof(Ts)...); ,编译时sizeof会更好 - 尽管点保持不变,sum<>sum更可重用。

答案 1 :(得分:0)

您可以使用auto和接受参数包的函数来生成必需的元组。例如,std::make_tuple工作正常:

#include <string>
#include <utility>

char const* format_value(double x) { /* ... */ }
std::string format_value(long x) { /* ... */ }

template <typename Sink, typename... Values>
Sink& format(Sink& target, Values... values)
{
    auto slices = std::make_tuple(format_value(values)...);
    /* Code that does things with the results. */
}

同样,您也可以通过转发到另一个函数来完全避免使用元组。例如:

#include <string>

char const* format_value(double x) { /* ... */ }
std::string format_value(long x) { /* ... */ }

template <typename Sink, typename... Results>
void format_impl(Sink& target, Results... results)
{
    /* Code that does things with the results. */
}

template <typename Sink, typename... Values>
Sink& format(Sink& target, Values... values)
{
    format_impl(target, format_value(values)...);
    return sink;
}