LoginSignup
5

More than 5 years have passed since last update.

Boost.RangeのOven拡張を自作してみる

Posted at

この記事は C++ Advent Calendar 2015 の24日目の記事です。

最近の仕事ではもっぱらPHPばかり書いてますが、C++も触れていたいなーという事で参加しました。

前書き

既存のライブラリとしてPStade.Ovenや高橋晶さんのBoost.Range拡張ライブラリがありますが、もっとシンプルで、アプリ毎の専用関数として使いたい、という理由と自分の勉強がてらに作成しています。

GitHubへのリンク

使い方

#include "range.h"

auto v = {1, 2, 3, 4, 5};
// {2, 4}
auto q = v | range::filtered_even();

BoostのRange Adaptorsと同じくパイプラインによって繋げます。

(面倒な為、既存のboost::adaptorsライブラリをrange::で使えるようにnamespace内で

using namespace boost::adaptors;

しています)

パイプライン記法で書けるように実装するには

パイプライン記法を使えるようにするには、operator|を糊付けとして、Range値(配列に限らない、begin()とend()を実装した範囲を表す値)と、Range値から新たなRange値を返す実装をする必要があります。

// Rangeから要素の型を得る
template <class Range>
auto _rangetype(Range&& r) ->
    typename std::decay<
        decltype(*std::begin(r))
    >::type;
// 配列要素の型取得
#define rangedecltype(r) decltype(_rangetype(r))

namespace range {
    //------------------------------
    /// INTERNAL ONLY
    /// 
    // {
        struct _FilteredEvenHolder {};

        template <typename T>
        struct _is_even {
            bool operator()(const T &t) const {
                return t % 2 == 0;
            }
        };
    // }

    _FilteredEvenHolder filtered_even() { return _FilteredEvenHolder(); }

    template <typename Range>
    auto operator| (Range &r, const _FilteredEvenHolder &)
    {
        return r | boost::adaptors::filtered(_is_even<rangedecltype(r)>());
    }

    template <typename Range>
    auto operator| (const Range &r, const _FilteredEvenHolder &)
    {
        return r | boost::adaptors::filtered(_is_even<rangedecltype(r)>());
    }
}

Boost.Range.Adaptorsのfilteredを特殊化している為filteredを利用していますが、boost::iterator_rangeを返すようにfilterdに当たる部分を自作も出来ます。

この形を基本形として、Range操作関係を全部range::hogeで済ませられるようにアプリライブラリを用意しておけば、実装時も読む時もシンプルに出来るんじゃないかな、という紹介でした。

range.hに始まる各ライブラリについて

作成の際にBoostのRangeの実装、PStade.Ovenの実装を多分に参考にしています。
2年程前に書いたC++11 & boost 1.49環境の当時のコードのままとなっていますが、今回のC++14 & boost 1.60の環境でも動作しました。

注意して作成してはいますが、何か問題がありましたらご報告をお願いします。

range::sorted・range::stable_sortedについて

これはかなり実験的な実装となっており、実装方法はPStade.Ovenのoven::sortedと同じなのですが、boost::iterator_range生成時にもととなるRangeをshared_ptrで保持する為、高コストな処理になっています、面倒ではありますが、一端vector化し、std::sortをした方が高速です。

まとめ

この記事は、 C++ Advent Calendar 2015 の24日目の記事でした。
明日は、Reputelessさんの記事、「ゲームとメディアアートのための C++ ライブラリ「Siv3D」」です。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5