可能重复:
Pretty-print std::tuple
在数据库库(soci)中,下面有一块代码可以在std::tuple<>
上使用一到十个参数。
静态类方法from_base()
和to_base()
是针对1元组到10元组的参数实现的。
guts基本上将每个n元组元素传入传入的流中。一切都是硬编码的。
如何将此代码转换为使用C ++ 11的可变参数模板(参数没有限制)? 实际上使用可变参数模板是次要的。我们真正想做的是将硬编码替换为n元组参数的一般情况。
部分问题是,从技术上讲,只有一个参数,但该参数是一个n元组,所以我不能完全使用here in 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
}
RUNNABLE ANSWER(基于Grizzly的帖子)
#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 );
}
答案 0 :(得分:5)
如果我正确理解了您的问题,您基本上需要分别为您的元组的每个元素调用运算符<<
或>>
。在这种情况下,您可以使用部分特化和递归来构造类似于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);
}
};
当然你可以更一般地写这个,但这会使源更加复杂,并且可能不会给你带来太多回报。
答案 1 :(得分:3)
我喜欢indices trick:
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
也一样。 (Live example based on jrok's.)
代码“利用”可变参数解包机制来准确地out << get<Is>(t)
sizeof...(Is)
(与sizeof...(Ts)
}次相同。 gen_seq<N>
生成从0
到N-1
(seq<0, 1, 2, ..., N-1>
)的编译时整数列表。此列表会保存到unsigned... Is
,然后会在get<Is>(t)
至get<0>(t)
,get<1>(t)
中解压缩,直至get<N-1>(t)
。
Alias<char[]>{ ... }
(临时数组)提供了可以使用包扩展的上下文。我在这里特别选择了数组初始化,因为保证了传递参数的从左到右的评估。 (void(expression), '0')
将评估expression
,放弃该值,并将'0'
作为初始化程序传递(void(...)
用于强制选择内置逗号运算符,并且没有任何可能的超载)。