パラメーターパックのグループ化またはペア化された折り畳みを作成する方法は?


14
template<class Msg, class... Args>
std::wstring descf(Msg, Args&&... args) {
    std::wostringstream woss;

    owss << Msg << ". " << ... << " " << args << ": '" << args << "' ";//not legal at all

    //or

    owss << Msg << ". " << args[0] << ": '" << args[1] << "'  " << args[2] << ": '" << args[3] << "' "; //... pseudo code, and so on...
}

代わりにペアのリストなどを使用できることはわかっていますが、関数の構文を維持しながらこれを行う方法に興味があります。

const auto formatted = descf(L"message", "arg1", arg1, "arg2", arg2);

回答:


9

折りたたみ式が使える!それは最も美しい*ではありませんが、提示されているすべての非折りたたみソリューションよりも短いです:

template<class T, class ... Args>
std::wstring descf(T msg, Args&&... args) {
    std::wostringstream owss;
    owss << msg << ". ";

    std::array<const char*, 2> tokens{": '", "' "};
    int alternate = 0;
    ((owss << args << tokens[alternate], alternate = 1 - alternate), ...);

    return owss.str();
}

サンプル出力を使用したデモ:https : //godbolt.org/z/Gs8d2x

コンマ演算子のフォールドを実行します。各オペランドは、1つのargsトークンと代替トークンの出力に加えて、トークンインデックスを切り替えます(後者の2つは別のコンマ演算子と組み合わされます)。

*折りたたみ式(およびカンマ演算子)をよく知っている読者には、これはおそらく「最良の」コードですが、他のすべての人にとってはまったく意味不明なので、コードベースにこれを適用するかどうかを自分で判断してください。


これはbool(ペアリングのみが必要な場合)alaでも機能すると思います。:b ^ = true; そしておそらく多項演算子(b? ": '"、 ":"' ")
darune

1
@darune確かに、交替を表現する他の方法があります。出力/代替ロジックを実際のトークン値から分離することにしました。これにより、配列はうまく機能します。インデックス作成時のからboolへの暗黙的な変換が嫌いなintので、実際intに状態を切り替えました。そして、前置対後置++は、検証するのに余分なメンタルサイクルを必要としますが(少なくとも私にとっては)、別のもの1 - を実際に誤読することはできません。要するに、私はこれを可能な限り読みやすくするように努めましたが、これはもちろん個人的な好み(または適切なスタイルガイド)に依存します。max66はそれをさらに凝縮しました。
Max Langhof

std::arrayネイティブ配列の代わりにを使用すると、意味のない複雑さのように見えます。
Deduplicator

私が見つけるよう@Deduplicator私は、強く反対するstd::array<const char*, 2>無限よりも読みやすいですconst char**。しかし、再び、これは私のかなりあいまいな構文周りの読みやすさで最高のショットは、あなたがあなた自身のコード内で好きなことを行うことができます。私ができることは、私が読みやすいと考えるデータポイントを提供することだけです。
Max Langhof

9

これは、次のパターンに従ういくつかのヘルパー関数で簡単です。

void helper() {}

template <class T1, class T2, class ... T>
void helper(T1 t1, T2 t2, T ... t)
{
     do_single_pair(t1, t2);
     helper(t...);
}

これはフォールド式ではありませんが、最終的な結果は同じです。


テンプレートの再帰の深さは、fold式の場合と異なりますか?それとも同じでしょう
ダルーン

1
@daruneフォールド式には固有の再帰はありません...フォールド式は(可変テンプレートの特定のインスタンス化で)ある式に正式に拡張されます。
Max Langhof

6

インデックスと三項演算子を使って試すことができると思います。

次のようなもの

template <typename ... Args>
std::wstring descf (std::wstring const & Msg, Args && ... args)
 {
   std::wostringstream woss;

   int i = 0;

   ((woss << Msg << ". "), ... ,(woss << args << (++i & 1 ? ": '" : "' ")));

   return woss.str();
 }

@MaxLanghofこれには、より多くのセパレーターに簡単に拡張できるという利点があります。
Deduplicator

@Deduplicator私はあなたが何を言っているのか理解できませんか?説明できる?
Max Langhof

@Deduplicator-「より多くのセパレータへの拡張」とはどういう意味か私には明確ではありません...とにかく...この解決策は受け入れられたものと非常に似ています。私はそれが多かれ少なかれ拡張可能だとは思いません。私はそれが少し(少し!おそらくコンパイラが同じ方法で最適化する)軽くstd::arrayなると思います(それはとにかく軽いクラスです)の使用を避けるためですが(受け入れられた答えが望ましいので)読みにくくなります。
max66

2

次のコードでうまくいくはずです。パラメータパックは、初期化リストで展開されます。

#include <string>
#include <iostream>
#include <sstream>
#include <vector>

template <typename...Args>
std::string descf(std::string msg, Args &&... args)
{
   auto argumentsVector = std::vector<std::string>{args...};

   std::stringstream ss;
   ss << msg << ". ";

   for (auto i = std::size_t{0}; i < argumentsVector.size() - 1; ++i)
      ss << argumentsVector[i] << ": '" << argumentsVector[i+1] << "' ";

   auto result = ss.str();
   if (!argumentsVector.empty())
       result.pop_back();
   return result;
}

int main()
{
   std::cout << descf("message", "arg1", "1", "arg2", "2") << std::endl;
}

これには、すべてargsをに変換できる必要がありますstd::string
クルミ

@walnut、それは正しいです。これが要件ではない場合は、式/再帰を折りたたむ必要があります
Mattias De Charleroy

1

std::index_sequence

template <class Msg, class... Pairs>
std::wstring descf_pair(const Msg& msg, const Pairs&... pairs)
{
    std::wstringstream woss;

    woss << msg << ". ";
    auto sep = L"";
    ((woss << sep << std::get<0>(pairs) << L": '"
                  << std::get<1>(pairs) << L"'", sep = L"  "), ...);
    return woss.str();
}

template <class Msg, std::size_t... Is, class Tuple>
decltype(auto) descf_impl(const Msg& msg, std::index_sequence<Is...>, Tuple&& t)
{
    return descf_pair(msg, std::tie(std::get<2 * Is>(t), std::get<2 * Is + 1>(t))...);
}

template <class Msg, typename ... Ts>
std::wstring descf(const Msg& msg, const Ts&... ts)
{
    static_assert(sizeof...(Ts) % 2 == 0);

    return descf_impl(msg,
                      std::make_index_sequence<sizeof...(Ts) / 2>(),
                      std::tie(ts...));
}

デモ

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.