2017-07-13 16 views
2

多くの種類の非常に使いやすいToString関数が必要です。std::tupleを含めてください。機能は次のようにされていますC++ 11 `std :: string ToString(std :: tuple <Args...>&t)`の実装方法は?

template <typename T> 
inline std::string ToString(const T &t) { 
    std::stringstream ss; 
    ss << t; 
    return ss.str(); 
} 

template <typename... Args> 
inline std::string ToString(const std::tuple<Args...> &t) { 
    std::stringstream ss; 
    for (int i = 0; i < t.size(); i++) { 
     ss << ToString(std::get<i>(t)) << " "; 
    } 
    return ss.str(); 
} 

第二部では、C++ 11のテンプレートとそれを実装する方法、文法上間違っていますか?

そして、どのようにこのようなFromStringを実装する:

template <typename T> 
inline T FromString(const std::string &s) { 
    std::stringstream ss(s); 
    T t; 
    ss >> t; 
    return t; 
} 

template <typname... Args> 
inline std::tuple<Args...> FromString(const std::string &s) { 
    std::tuple<Args...> ret; 
    ret.resize(sizeof...Args); 
    std::stringstream ss; 
    size_t pos; 
    for (int i = 0, prev_pos = 0; i < sizeof...Args and prev_pos < s.length(); i++) { 
     pos = s.find(" ", prev_pos); 
     T t = FromString(s.substr(prev_pos, pos)); 
     std::get<i>(ret) = t; 
     prev_pos = pos 
    } 
    return ret; 
} 

2番目の部分は、それを実装する方法を、C++ 11の文法にも間違っていますか? C++ 17では

+0

テンプレートは*コンパイル時のみ*です。ランタイム変数をテンプレート引数として使用することはできません。これを解決するには、パラメータパックを受け取るヘルパー関数を用意し、[タプルを展開する]ことをお勧めします(https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching -function-pointer)をヘルパー関数の呼び出しで使用します。 –

+3

テンプレートメタプログラミングでは、反復は通常、再帰を使用して行われます。 –

+0

私はC++のテンプレートが良くないので、コードを正確に記述する方法がわかりません...コードを表示しますか? – linrongbin

答えて

7

、あなたが行うことがあります。

template <typename ... Ts> 
std::string ToString(const Ts& ... ts) { 
    std::stringstream ss; 
    const char* sep = ""; 
    ((static_cast<void>(ss << sep << ts), sep = " "), ...); 
    return ss.str(); 
} 

template <typename... Args> 
std::string ToString(const std::tuple<Args...> &t) { 
    return std::apply([](const auto&... ts) { return ToString(ts...); }, t); 
} 

Demo

+0

クールですが、C++ 17はやや攻撃的です。 – linrongbin

+0

@Holt末尾の空白 –

+0

@PasserByオリジナルのバージョンは末尾の空白を削除しませんでしたが、更新されたものもあります;) – Holt

4
namespace notstd { 
    template<std::size_t...Is> 
    struct index_sequence {}; 
    template<std::size_t N, std::size_t...Is> 
    struct make_index_sequence:make_index_sequence<N-1,N-1,Is...>{}; 
    template<std::size_t...Is> 
    struct make_index_sequence<0,Is...>:index_sequence<Is...>{}; 

#define RETURNS(...) \ 
    noexcept(noexcept(__VA_ARGS__)) \ 
    -> decltype(__VA_ARGS__) \ 
    { return __VA_ARGS__; } 

    namespace details { 
    template<class F, class Tuple, std::size_t...Is> 
    auto apply(F&& f, Tuple&& tuple, index_sequence<Is...>) 
    RETURNS(std::forward<F>(f)(std::get<Is>(std::forward<Tuple>(tuple))...)) 
    template<class Tuple> 
    using raw_tuple = typename std::remove_cv<typename std::remove_reference<Tuple>::type>::type; 
    template<class Tuple> 
    using tuple_count = std::tuple_size< raw_tuple<Tuple> >; 
    } 
    template<class F, class Tuple> 
    auto apply(F&& f, Tuple&& tuple) 
    RETURNS(
    ::notstd::details::apply(
     std::forward<F>(f), 
     std::forward<Tuple>(tuple), 
     ::notstd::make_index_sequence< 
     ::notstd::details::tuple_count<Tuple>::value 
     >{} 
    ) 
) 
} 

を今、この::notstd::applyはC++ 17のstd::applyのように多くのことを振る舞います。

私たちは、あなたのToStringToStream経由にこれをのり:これも再帰的タプルを平ら

struct to_stream_t; 

template<class...Args> 
void ToStream(std::ostream& os, const std::tuple<Args...>& t) { 
    os << '{'; 
    ::notstd::apply(to_stream_t{os}, t); 
    os << '}'; 
} 
inline void ToStream(std::ostream&) {} 
template<class T> 
void ToStream(std::ostream& os, const T& t) { 
    os << t; 
} 
template<class T0, class... Ts> 
void ToStream(std::ostream& os, const T0& t0, const Ts& ... ts) { 
    ToStream(os, t0); 
    using discard=int[]; 
    (void)discard{0,((
    void(os << ' '), to_stream_t{os}(ts) 
),0)...}; 
} 
struct to_stream_t { 
    std::ostream& os; 
    template<class...Args> 
    void operator()(Args const&...args) const { 
    ToStream(os, args...); 
    } 
}; 
template<class...Ts> 
std::string ToString(Ts const&... ts) { 
    std::stringstream ss; 
    ToStream(ss, ts...); 
    return ss.str(); 
} 

あなたがstd以上または基本的なタイプのマニュアルToStream実装を追加する場合は、to_stream_tまたは再帰の前に体が動作しませんそれらを置きます。そして通常ToStream(os, t)の代わりにto_stream_t{os}(t)を経由して再帰するので、適切なオーバーロードを見つけることができます。

テストコード:

std::tuple<std::string, std::string, int> t("hello", "world", 42); 
std::cout << ToString(t, "-", t); 

Live example

我々は、ベクターのサポートを強化することができます。その後、

template<class T, class A> 
void ToStream(std::ostream& os, const std::vector<T, A>& v) { 
    os << '['; 
    for (auto const& x:v) 
    { 
    if (std::addressof(x) != v.data()) 
     os << ','; 
    to_stream_t{os}(x); 
    } 
    os << ']'; 
} 

このすべてでテスト:

std::tuple<std::string, std::string, int> t("hello", "world", 42); 
std::cout << ToString(t, "-", t) << "\n"; 
std::vector<int> v {1,2,3}; 
std::cout << ToString(v) << "\n"; 
std::vector< std::tuple<int, int> > v2 {{1,2},{3,4}}; 
std::cout << ToString(v2) << "\n"; 
auto t2 = std::tie(v, v2); 
std::cout << ToString(t2) << "\n"; 

Live example

エンド出力は、次のとおり

{hello world 42} - {hello world 42} 
[1,2,3] 
[{1 2},{3 4}] 
{[1,2,3] [{1 2},{3 4}]} 

予想通り。あなたが をあきらめ、この

#include<iostream> 
#include<tuple> 
#include<utility> 
#include<sstream> 

template<size_t... I> 
struct index_sequence {}; 

template<size_t N, size_t sz, size_t... I> 
struct make_index_sequence_ 
{ 
    using type = typename make_index_sequence_<N, sz + 1, I..., sz>::type; 
}; 

template<size_t N, size_t... I> 
struct make_index_sequence_<N, N, I...> 
{ 
    using type = index_sequence<I...>; 
}; 

template<size_t N> 
using make_index_sequence = typename make_index_sequence_<N, 0>::type; 

template<typename Fn, typename Tuple, size_t... I> 
auto apply_(Fn&& fn, Tuple&& tup, index_sequence<I...>) -> decltype(fn(std::get<I>(tup)...)) 
{ 
    return fn(std::get<I>(tup)...); 
} 

template<typename Fn, typename Tuple> 
auto apply(Fn&& fn, Tuple&& tup) -> decltype(apply_(std::forward<Fn>(fn), std::forward<Tuple>(tup), make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type>::value>{})) 
{ 
    return apply_(std::forward<Fn>(fn), std::forward<Tuple>(tup), make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type>::value>{}); 
} 

に上記のすべてのものをやってみたいことがC++ 11では

+0

@holtの入力ミスが修正されました。私は 'ToStream'から' ToStream'に移行して一時的な 'std :: string'sを削除し、いくつかのものを逃しました。 – Yakk

+0

@holt再帰的なタプルをサポートしたい場合は、 'ToStream'がそれをサポートする必要がありました。そして、それをサポートしていません! – Yakk

+0

@holt検索に問題がある可能性があります。 'to_stream_t'は、' ToStream'が宣言された後に 'to_stream_t'の中の' ToStream'のルックアップを行う場所を*に移動します。ラムダを使用する場合は、 'ToStream'sがラムダの後ろに宣言されていることを見つけるためにADLが必要です。私はADLトークンを使うことができますが、それは面倒です。 'to_stream_t'は簡単に思えます。 – Yakk

1

は、新しいC++で再実装標準ライブラリです。

template<typename T> 
std::string ToString(const T& t) 
{ 
    std::stringstream ss; 
    ss << t; 
    return ss.str(); 
} 

template<typename T, typename... Ts> 
std::string ToString(const T& t, const Ts&... ts) 
{ 
    return ToString(t) + ToString(ts...); 
} 

template<typename... Ts> 
std::string ToString(const std::tuple<Ts...>& tup) 
{ 
    return apply<std::string (*)(const Ts&...)>(ToString, tup); 
} 

これは本当のロジックです。

Live

構文糖がどれだけ偉大な全く新しいレベルに私に感謝を与えました。

関連する問題