11

C++ で遅延変換のパイプラインを作成するために、Boost::Range を使い始めました。私の問題は、パイプラインを小さな部分に分割する方法です。私が持っているとします:

int main(){
  auto map = boost::adaptors::transformed; // shorten the name
  auto sink = generate(1) | map([](int x){ return 2*x; })
                          | map([](int x){ return x+1; })
                          | map([](int x){ return 3*x; });
  for(auto i : sink)
    std::cout << i << "\n";
}

そして、最初の 2 つのマップを に置き換えたいmagic_transform、つまり:

int main(){
  auto map = boost::adaptors::transformed; // shorten the name
  auto sink = generate(1) | magic_transform()
                          | map([](int x){ return 3*x; });
  for(auto i : sink)
    std::cout << i << "\n";
}

どのように書くmagic_transformでしょうか?Boost::Range のドキュメントを調べたのですが、よくわかりません。

補遺:私はこのようなクラスを書きたいと思っています:

class magic_transform {
    ... run_pipeline(... input) {
        return input | map([](int x){ return 2*x; })
                     | map([](int x){ return x+1; });
};
4

2 に答える 2

6

最も難しい問題は、コード内の戻り型を把握することです。decltypeラムダはうまく混合されないため(ここを参照)、別の方法を考える必要があります。

auto map = boost::adaptors::transformed;

namespace magic_transform
{
   std::function<int(int)> f1 = [](int x){ return 2*x; };
   std::function<int(int)> f2 = [](int x){ return x+1; };
   template <typename Range>
   auto run_pipeline(Range input) -> decltype(input | map(f1) | map(f1))
   {
        return input | map(f1) | map(f2);
   }
}

...
auto sink = magic_transform::run_pipeline(generate(1))
                          | map([](int x){ return 3*x; });

簡単な解決策は、ラムダをに貼り付けて、リターンタイプを推測するstd::functionために使用できるようにすることです。decltypeこの例では名前空間を使用magic_transformしましたが、必要に応じてこのコードをクラスに適合させることもできます。これがあなたのコードを上記に適応させるリンクです。

また、std::functionここで使用するのはやり過ぎかもしれません。代わりに、代わりに2つの通常の関数を宣言することができます()。

私も実験していましboost::any_rangeたが、C + 11ラムダなどとの非互換性があるようです。私が得ることができた最も近いものは次のとおりです():

auto map = boost::adaptors::transformed;
using range = boost::any_range<
               const int,
               boost::forward_traversal_tag,
               const int&,
               std::ptrdiff_t
               >;

namespace magic_transform
{
    template <typename Range>
    range run_pipeline(Range r)
    {
        return r | map(std::function<int(int)>([](int x){ return 2*x; }))
             | map(std::function<int(int)>([](int x){ return x+1; }));
    }
}

int main(){
  auto sink = magic_transform::run_pipeline(boost::irange(0, 10))
                          | map([](int x){ return 3*x; });
  for(auto i : sink)
    std::cout << i << "\n";
}
于 2012-11-06T03:02:48.557 に答える
1

私はうまくいくと思います:

auto magic_transform()->decltype(boost::adaptors::transformed(std::function<int(int)>())
{
    std::function<int(int)> retval = [](int x){ return [](int x){ return x+1; }(2*x);
    return boost::adaptors::transformed(retval);
}

しかし、それはおそらくあなたが探しているものではありません。:) (上記のコードのジョーク: 2*x+1 のチェーンされたラムダ、基本的に実装で decltype を使用して戻り値の型を見つける)、

のソース コードを見ると、戻りたいhttp://www.boost.org/doc/libs/1_46_1/boost/range/adaptor/transformed.hpp型は です。ここで、T は関数の型です。magic_transformboost::range_detail::transform_holder<T>

ラムダを使用してスタックで実行すると、T非常に狭い型になります。すべての詳細を公開せずに抽象的な変換を渡したい場合は、 を使用するのstd::function<outtype(intype)>が合理的かもしれません (わずかな実行時のオーバーヘッドが発生します)。

うまくいくことを願っています。

于 2012-11-05T23:35:39.280 に答える