読者です 読者をやめる 読者になる 読者になる

きままにブログ

プログラミングを主とした私のメモ帳です。寂しいのでコメントください笑

rangeクラスを作り、アダプタを適用していく例

C++

前回の記事の続きですが、作ったアダプタを例えば範囲クラスrangeに適用してみたいと思います。今回は簡単のためイテレータを実装せず、getメンバ関数でrangeを0から順番に回していきます。しかも範囲チェックはしていません。

Step1

まずはアダプタがない状態の範囲クラスrangeを見てみましょう :

class no_adaptor { };

template <>
class range<no_adaptor> {
public:
	range(int i_start, int i_end) : i_start(i_start), i_end(i_end), position(0) {}

	int get() { // イテレータを模す
		int ret =  i_start + position;
		++position;
		return ret;
	}

	int i_start;
	int i_end;
	int position;
};

ご覧のとおりイテレータを内蔵したようなしょうもないクラスですが、getでどんどん取得できるという点が重要です。

Step2

このrangeにアダプタを適用できるように次のメンバ関数を追加しておきます。

template <typename Adaptor>
auto operator>>(Adaptor adaptor) {
	return range<Adaptor>(*this, adaptor);
}

Step3

ではアダプタのあるrangeのクラスを作ります。

template <typename Adaptor>
class range {
public:
	template <typename ItsAdaptor>
	range(range<ItsAdaptor> const& it, Adaptor adaptor) : i_start(it.i_start), i_end(it.i_end), position(it.position), adaptor(adaptor) {}

	int get() { // イテレータを模す
		int ret = i_start + position;
		++position;
		return adaptor(ret); // ここで適用
	}

	Adaptor adaptor;
	int i_start;
	int i_end;
	int position;
};

別のアダプタを持ったrangeをコピーし、新しいアダプタを追加していく例です。

Step4

さらにアダプタを合成できるように次のメソッドを導入します。

template <typename ItsAdaptor>
auto operator>>(ItsAdaptor adaptor) {
	return range<mix_adaptor<Adaptor, ItsAdaptor>>(*this, this->adaptor >> adaptor);
}

実行例

それでは実行例を見てみましょう :

int main() {
	auto r = range<no_adaptor>(1, 10)
		>> make_adaptor([](int n) { return n * n; })
		>> make_adaptor([](int n) { return n + 1; });

	printf("%d\n", r.get()); // 1*1+1 = 2
	printf("%d\n", r.get()); // 2*2+1 = 5
	printf("%d\n", r.get()); // 3*3+1 = 10

	getchar();
	return 0;
}

実装例

class no_adaptor { };

template <typename Adaptor>
class range {
public:
	template <typename ItsAdaptor>
	range(range<ItsAdaptor> const& it, Adaptor adaptor) : i_start(it.i_start), i_end(it.i_end), position(it.position), adaptor(adaptor) {}

	int get() {
		int ret = i_start + position;
		++position;
		return adaptor(ret);
	}

	template <typename ItsAdaptor>
	auto operator>>(ItsAdaptor adaptor) {
		return range<mix_adaptor<Adaptor, ItsAdaptor>>(*this, this->adaptor >> adaptor);
	}

	Adaptor adaptor;
	int i_start;
	int i_end;
	int position;
};

template <>
class range<no_adaptor> {
public:
	range(int i_start, int i_end) : i_start(i_start), i_end(i_end), position(0) {}

	int get() {
		int ret =  i_start + position;
		++position;
		return ret;
	}

	template <typename Adaptor>
	auto operator>>(Adaptor adaptor) {
		return range<Adaptor>(*this, adaptor);
	}

	int i_start;
	int i_end;
	int position;
};

追記

イテレータを実装してみました。begin, endで呼び出せば、次のように使えます((n*n)+1を1~10で回す例) :

auto r = range(1, 10)
	>> make_adaptor([](int n) { return n * n; })
	>> make_adaptor([](int n) { return n + 1; });

for (auto&& it : r) {
	printf("%d\n", it);
}

実装

template <typename Adaptor>
class range_iterator : public std::iterator<std::input_iterator_tag, int> {
public:
	range_iterator(range<Adaptor>* r, int position) : r(r), position(position) {}
	range_iterator(range_iterator const& it) : r(it.r), position(it.position) {}
	auto operator*() const {
		return r->adaptor(position);
	}
	auto operator->() const {
		return r->adaptor(position);
	}
	range_iterator& operator++() {
		++position;
		return *this;
	}
	range_iterator& operator++(int) {
		auto ret = *this;
		++position;
		return ret;
	}
	bool operator==(const range_iterator& it) const {
		return it.position == this->position;
	}
	bool operator!=(const range_iterator& it) const {
		return it.position != this->position;
	}
	range<Adaptor>* r;
	int position;
};