2

可能重复:
漂亮打印 std::tuple

在数据库库 (soci) 中,下面有一大段代码可以处理std::tuple<>1 到 10 个参数。

静态类方法from_base(),并to_base()为 1-tuple 到 10-tuple 的参数实现。

胆量基本上将每个 n 元组元素流进和从传入的流中流出。一切都是硬编码的。

如何将此代码转换为使用 C++11 的可变参数模板(对参数没有限制)? 实际上是否使用可变参数模板是次要的。我们真正想做的是用 n 元组参数的一般情况替换硬编码。

部分问题是,从技术上讲,只有一个参数,但该参数是一个 n 元组,所以我不能完全使用Wikipedia 中描述的内容。最好的方法是什么?

#include "values.h"
#include "type-conversion-traits.h"
#include <tuple>

namespace soci
{

template <typename T0>
struct type_conversion<std::tuple<T0> >
{
    typedef values base_type;

    static void from_base(base_type const & in, indicator ind,
        std::tuple<T0> & out)
    {
        in
            >> std::get<0>(out);
    }

    static void to_base(std::tuple<T0> & in,
        base_type & out, indicator & ind)
    {
        out
            << std::get<0>(in);
    }
};

template <typename T0, typename T1>
struct type_conversion<std::tuple<T0, T1> >
{
    typedef values base_type;

    static void from_base(base_type const & in, indicator ind,
        std::tuple<T0, T1> & out)
    {
        in
            >> std::get<0>(out)
            >> std::get<1>(out);
    }

    static void to_base(std::tuple<T0, T1> & in,
        base_type & out, indicator & ind)
    {
        out
            << std::get<0>(in)
            << std::get<1>(in);
    }
};

// ... all the way up to 10 template parameters

}

可运行的答案(基于下面灰熊的帖子)

#include <iostream>
#include <tuple>

using namespace std;

// -----------------------------------------------------------------------------

template<unsigned N, unsigned End>
struct to_base_impl 
{
    template<typename Tuple>
    static void execute(Tuple& in, ostream& out) 
    {
      out << std::get<N>(in) << endl;
      to_base_impl<N+1, End>::execute(in, out);
    }
};

template<unsigned End>
struct to_base_impl<End, End>
{ 
    template<typename Tuple>
    static void execute(Tuple& in, ostream& out) 
    {
      out << "<GAME OVER>" << endl;
    }
};

// -----------------------------------------------------------------------------

template <typename Tuple>
struct type_conversion
{
    static void to_base(Tuple& in, ostream& out )
    {
        to_base_impl<0, std::tuple_size<Tuple>::value>::execute(in, out);
    }
};

template <typename... Args>
struct type_conversion<std::tuple<Args...>>
{
    static void to_base(std::tuple<Args...>& in, ostream& out )
    {
        to_base_impl<0, sizeof...(Args)>::execute(in, out);
    }
};

// -----------------------------------------------------------------------------

main()
{
    typedef tuple<double,int,string> my_tuple_type;
    my_tuple_type t { 2.5, 5, "foo" };

    type_conversion<my_tuple_type>::to_base( t, cerr );
}
4

2 回答 2

5

如果我正确理解您的问题,您基本上需要为元组的每个元素分别调用运算符<<或on in 。>>在这种情况下,您可以使用部分特化和递归构造类似于 for 循环的东西(有点,因为它实际上每次调用不同的函数):

template<unsigned N, unsigned End>
struct to_base_impl {
    template<typename Tuple>
    void execute(Tuple& in, base_type& out) {
      out<<std::get<N>(in);
      to_base_impl<N+1, End>::execute(in, out);
    }
};

template<unsigned End>
struct to_base_impl<End, End> { //End of loop
    template<typename Tuple>
    void execute(Tuple& in, base_type& out) {}
};

template <typename Tuple>
struct type_conversion
{
    typedef values base_type;

    static void to_base(Tuple& in, base_type & out, indicator & ind){
        to_base_impl<0, std::tuple_size<Tuple>::value>::execute(in, out);
    }
};

这将从零迭代到元组的大小并调用out<<std::get<N>(in);每次迭代。from_base仅使用in>>std::get<N>(out);. 如果你想确保你的转换器只用元组调用,你可以使用可变参数模板:

template <typename... Args>
struct type_conversion<std::tuple<Args...>>
{
    typedef values base_type;

    static void to_base(std::tuple<Args...>& in, base_type & out, indicator & ind){
        to_base_impl<0, sizeof...(Args)>::execute(in, out);
    }
};

当然,您可以更通用地编写此代码,但这会使源代码更加复杂,并且可能不会给您带来太多回报。

于 2012-11-06T17:48:04.873 回答
3

我喜欢索引技巧

template<unsigned...> struct seq{};
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...>{};

template<class T> using Alias = T;

template<class... Ts, unsigned... Is>
void to_base(std::tuple<Ts...> const& t, base_type& out, seq<Is...>){
  Alias<char[]>{ (void(out << std::get<Is>(t)), '0')... };
}

template<class... Ts, unsigned... Is>
void to_base(std::tuple<Ts...> const& t, base_type& out){
  to_base(t, out, gen_seq<sizeof...(Ts)>{});
}

对于from_base. (基于 jrok 的实时示例。

代码“利用”可变参数解包机制来out << get<Is>(t)精确调用sizeof...(Is)(与 相同sizeof...(Ts))时间。从to ( )gen_seq<N>生成一个编译时整数列表。此列表保存到,然后将其解压缩到, ,一直到. (一个临时数组)提供了一个可以使用包扩展的上下文。我在这里特别选择了数组初始化,因为可以保证对传递的参数进行从左到右的评估。将评估,丢弃值,并作为初始化程序传递(用于强制选择内置逗号运算符,而不是任何可能的重载)。0N-1seq<0, 1, 2, ..., N-1>unsigned... Isget<Is>(t)get<0>(t)get<1>(t)get<N-1>(t)
Alias<char[]>{ ... }(void(expression), '0')expression'0'void(...)

于 2012-11-06T19:15:45.337 回答