2

重複の可能性:
Pretty-print std::tuple

データベース ライブラリ (soci) には、std::tuple<>1 ~ 10 個のパラメーターで動作する以下のコードのチャンクがあります。

静的クラス メソッドfrom_base()でありto_base()、1 タプルから 10 タプルの引数に対して実装されます。

ガッツは基本的に、渡されたストリームとの間ですべての n タプル要素をストリーミングします。すべてがハードコードされています。

代わりに C++11 の可変個引数テンプレートを使用するようにこのコードを変換するにはどうすればよいですか (パラメーターに制限はありません)。 実際に可変長テンプレートを使用するかどうかは二次的なものです。私たちが本当にやりたいことは、ハードコーディングを n タプル引数の一般的なケースに置き換えることです。

問題の一部は、技術的には引数が 1 つしかないことですが、その引数は n タプルであるため、ウィキペディアで説明されている内容を完全に使用することはできません。最善のアプローチは何ですか?

#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 );
}
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)) 回呼び出します。から( )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 に答える