Hatena::ブログ(Diary)

wordiの日記 このページをアンテナに追加 RSSフィード

2005 | 01| 02| 03| 04| 05| 06| 07| 08| 09| 10| 11| 12|
2006 | 01| 02| 03| 04| 05| 06| 07| 08| 09| 10| 11| 12|
2007 | 01| 02| 03| 04| 05| 06| 07| 08| 09| 10| 11| 12|
2009 | 01| 02| 03| 04| 05| 06| 07| 08| 09| 10| 11| 12|
2010 | 01| 02| 03| 04| 05| 06| 07| 08| 09| 10| 11| 12|
2011 | 01| 02| 03| 04| 05| 06| 07| 08| 09| 10| 11| 12|
2012 | 01| 02| 03| 04| 05| 06| 07| 08| 09| 10| 11| 12|
2013 | 01| 02| 03| 04| 05| 06| 07| 08| 09| 10| 11|
iPhone プログラミング D言語 日記 ゲーム 仕事 出来事 ねた ソフトウェア 囲碁

2013-11-28

[]C++14関数戻り値型推論 C++14関数の戻り値の型推論を含むブックマーク C++14関数の戻り値の型推論のブックマークコメント

戻り値型推論について

C++14から戻り値型推論が使えるようになり、例えば、以下の関数も正常に動作します。

auto getThree() { return 3; } // 戻り値の型はint

Boost Range Ovenを使ったサンプル

上記だけだと何が便利かピンと来ないですが、複雑な型をautoだけで済ませられるようになったという事は、templateを駆使したクラスを戻り値として使うのが手軽になったという事です。

以下がサンプル、環境はGCC 4.8.1、Boost 1.55.0です。

※コンパイル時に-std=c++1yを渡しています。

#include <iostream>
#include <vector>

#include <boost/range/adaptors.hpp>

using namespace std;
using namespace boost;
using namespace boost::adaptors;

template <class Range>
std::vector<typename Range::value_type> to_vector(const Range &r) {
	return {std::begin(r), std::end(r)};
}

class Array {
public:
	Array(std::vector<int> v)
		: v(v)
	{
	}

private:
	auto overQuery(int min) {
		return v
			| filtered([min](int x) {
				return x >= min;
			});
	}

public:
	// 指定の値以上がある?
	bool existOver(int min) {
		// ※boost::emptyは、渡されたRangeのiteratorのbegin、endが同じ位置がチェックしてるだけ
		return not empty(overQuery(min));
	}

	// 指定の値以上の一覧を返す
	std::vector<int> toOver(int min) {
		return to_vector(overQuery(min));
	}

private:
	std::vector<int> v;
};

int main(){

	auto a = Array({1, 2, 3, 4, 5});

	for (auto x : a.toOver(3)) cout << x << " ";
	cout << endl;

	cout << a.existOver(2) << std::endl;
	cout << a.existOver(6) << std::endl;

	return 0;
}

実行結果

3 4 5
1
0

配列に対して、ある値以上を取得したり、存在してるかをチェックしています。

従来だと無駄な処理が発生しないようにexistOverとtoOverそれぞれに似たような処理を書く必要がありました。

それがBoot Range Ovenによって内部関数のoverQueryに纏められ、

無駄な処理を行わずにコードの冗長性を省く事が出来ました、こりゃ便利。

2013-11-16

[]C++LINQライクライブラリC#LINQ覚書き C++のLINQライクライブラリとC#のLINQ覚書きを含むブックマーク C++のLINQライクライブラリとC#のLINQ覚書きのブックマークコメント

概要

LINQを使うに当たって、評価コストやメモリ使用量が気になったので自分用メモ

C#LINQを基準にして考え、cpplinqBoost.RangeのOven拡張の使い方、特性について書いていきます。


C#LINQ

クエリはIEnumerableな配列として作成されます、

作成時にはクエリ式は評価されず、ループやCount()の時に評価されます。

var lst = new [] {1, 2, 3, 4, 5, 6};

int query_counter = 0;
var q = lst
	.Where(x => {
		query_counter++;
		Console.Write(" a:" + x);
		return x % 2 == 0;
	})
	.Where(x => {
		query_counter++;
		Console.Write(" b:" + x);
		return x <= 4;
	})
	.Select(x => {
		query_counter++;
		Console.Write(" c:" + x);
		return x * 2;
	})
	;
Console.WriteLine("maked query");

int loop_counter = 0;
foreach (var x in q) loop_counter++;
Console.WriteLine();

Console.WriteLine("query_counter:" + query_counter + " loop_counter:" + loop_counter);

結果

maked query
 a:1 a:2 b:2 c:2 a:3 a:4 b:4 c:4 a:5 a:6 b:6
query_counter:11 loop_counter:2

C++のcpplinqライブラリ

クエリ*1はbase_rangeを継承した各コンテナクラスで表現されます、

内部では配列iteratorをコンテナへ格納し、クエリ式毎に各クエリ用のコンテナへと評価用のファンクタと共に格納していきます。

スーパークラスのクラス図

f:id:wordi:20131116174858p:image

サブクラスbuilderによって作成され、スーパークラスのrangeとファンクタをメンバに持ってます。

ちなみにcpplinqではbase_rangeがiteratorを持っていない為か、begin、endのメソッドが実装されていない為range-based forが使えず、

ループの時にはfromとnextを使う必要があります。

using namespace std;
using namespace cpplinq;
	
auto v = {1, 2, 3, 4, 5, 6};

using namespace cpplinq;
int query_counter = 0;
auto q = from(v)
	>> where ([&query_counter](const int &x) {
		query_counter++;
		cout << " a:" << x;
		return x % 2 == 0;
	})
	>> where ([&query_counter](const int &x) {
		query_counter++;
		cout << " b:" << x;
		return x <= 4;
	})
	>> select([&query_counter](const int &x) {
		query_counter++;
		cout << " c:" << x;
		return x * 2;
	})
	;
cout << endl << "maked query" << endl;

int loop_counter = 0;
while (q.next()) { const auto &x = q.front(); loop_counter++; }
// ※ここでqの役目は終わる
cout << endl;

cout << "query_counter:" << query_counter << " loop_counter:" << loop_counter << endl;

結果

maked query
 a:1 a:2 b:2 c:2 a:3 a:4 b:4 c:4 a:5 a:6 b:6
query_counter:11 loop_counter:2

C++Boost.RangeのOven拡張

クエリ*2はboost_iterator_rangeを継承した各コンテナクラスで表現されます。

スーパークラスのクラス図

f:id:wordi:20131116184415p:image

サブクラススーパークラスによって作成され、スーパークラスiteratorファンクタをメンバに持ってます。

こっちはcpplinqと違い配列に対してそのまま適用出来、

他とは違い、事前に評価が可能な所は先に評価が実行されます。

auto v = {1, 2, 3, 4, 5, 6};

using namespace boost::adaptors;
using namespace std;

int query_counter = 0;
auto q = v
	| filtered([&query_counter](const int &x) {
		query_counter++;
		cout << " a:" << x;
		return x % 2 == 0;
	})
	| filtered([&query_counter](const int &x) {
		query_counter++;
		cout << " b:" << x;
		return x <= 4;
	})
	| transformed([&query_counter](const int &x) { // 1.54.0からtransformedにもラムダ式使える、素敵
		query_counter++;
		cout << " c:" << x;
		return x * 2;
	})
	;
cout << endl << "maked query" << endl;

int loop_counter = 0;
for (const auto &x : q) loop_counter++;
cout << endl;

cout << "query_counter:" << query_counter << " loop_counter:" << loop_counter << endl;

結果

 a:1 a:2 b:2
maked query
 c:2 a:3 a:4 b:4 c:4 a:5 a:6 b:6
query_counter:11 loop_counter:2

まとめ

C++ではBoost.RangeのOven拡張が定番。

コンパイル時間が気になるならcpplinq*3

・パフォーマンスとメモリ使用量は、どちらも内部でiterator*4進めてファンクタで評価してるだけなので大差ないはず*5

・書き方の違いはどちらもLINQライクなので結局は慣れ。

*1:文脈上こう呼びます、厳密には違う

*2:文脈上ry

*3:Ovenでも無視できる程度だと思う

*4:cpplinqは厳密には違うけど

*5:測定したら、最適化なしはcpplinqが二倍程速く、-O2で最適化すると差が無かった

2013-11-07

[]OpenFLで使えるGUIライブラOpenFLで使えるGUIライブラリを含むブックマーク OpenFLで使えるGUIライブラリのブックマークコメント

このページには保存版として使えるように、黙々とOpenFL*1で使えるGUIを張っていこうかと思ってます

StablexUI

2013/11/08記載

  1. GitHub
  2. ドキュメントも充実
  3. http://lib.haxe.org/に公開されてる中では一番規模大きい?

軽く触ってみた感じ一番高機能。

UIXMLに記述して、それをUIBuilderクラスに食わせるとXMLパースされてUIが生成される仕組み。

デモはスマホUI最適化されてるけど、ゲーム用に特定位置に表示させる事も出来るかも。

後で自分でもUIを書いてみる

haxeui

2013/11/08記載

  1. GitHub
  2. ドキュメント
  3. http://lib.haxe.org/にも公開

こっちはCSSXMLに分かれていて、

XMLはXMLControllerを継承したクラスを用意してパースする、

サンプルを見た感じ、XMLUIのパーツを書いてコード上でそのパーツを配置する、なんて使い方も出来るみたい。

後で自分でもUIを書いてみる

*1HaxeとOpenFLの使い分けに迷ってます、言語の時はHaxe、環境の時はOpenFLで良いのかな?

2013-11-04

[]C++LINQ C++でLINQを含むブックマーク C++でLINQのブックマークコメント

cpplinqというC++11でLinqライクな事が出来るライブラリを使ってみた、という内容です。

なので若干釣り記事です。

コンパイラGCC 4.8.1(MinGW)を使用しています。


cpplinq概要

cpplinqでは、操作用のクラスへRange受け取った後、>>演算子によってクエリ式を実行していき、最終的にlistやvectorで結果を受け取ります、

内部ではクエリ式ごとに専用のクラスへと変換されていきますが、必要分のメンバしか持たない為ある程度コストが抑えられています。

また、C#のLINGと同じく評価されるまでクエリ式が実行されない遅延評価が実装されてます。

auto v = {1, 2, 3};
auto q = from(v) >> where([](const int &x){ cout << "hoge"; return true; }); // まだ未評価なので、hogeは出力されない

結果出力(ここでhogeが3回出力)
for (const auto &x : q >> to_vector()) { ... }

サンプルコード

基本的な使い方はこうなります。

#include <iostream>
#include "cpplinq.hpp"

namespace cpplinq {
	// 固定長配列用
	// @note : from_arrayだとconstな固定長配列を使えない & 関数名を統一する為のヘルパ関数
	template<typename TValueArray>
	CPPLINQ_INLINEMETHOD detail::from_range<typename detail::get_array_properties<TValueArray>::iterator_type> from(
			TValueArray & a 
		) throw ()
	{
		typedef typename std::remove_const<typename std::remove_reference<decltype(*std::begin(a))>::type>::type type;
		return from_iterators(
			const_cast<type *>(std::begin(a)),
			const_cast<type *>(std::end(a))
		);
	}
	
	// initializer_list用
	// @note : initialize_listを直接渡せるようにする為のヘルパ関数
	//         usage : cppinq::from({1, 2, 3, 4, 5})
	template<typename T>
	CPPLINQ_INLINEMETHOD detail::from_range<typename std::initializer_list<T>::const_iterator> from(
			std::initializer_list<T> const &  list
		) throw ()
	{
		return from_iterators(
			list.begin(),
			list.end()
		);
	}
}

int main() {
	
	using namespace cpplinq;
	{
		auto v = {1, 2, 3, 4 ,5};
		// {12.56f, 15.7f}
		auto q = from(v)
			>> where ([](const int &x) { return x > 3; })
			>> select([](const int &x) -> float { return x * 3.14f; })
			>> where ([](const float &x) { return x > 12.0f; }) // ここでは意味ない
			;
	} {
		// {2, 4}
		auto q = from({1, 2, 3, 4, 5})
			>> where([](const int &x) { return x % 2 == 0; })
			;
	}
	return 0;
}

ラムダ式型推論を使う時

ラムダ式型推論を使いたいのですが、autoはC++11ではまだ未実装です、

decltypeがあるにはありますが、Select後等では型が変わる為適用が難しいです、

型が変わる時は推論しないというのも手ですが、一貫性に欠けます。

template <class Range>
auto _f_rangetype(Range &&rng) ->
	typename std::remove_reference<
		decltype(*std::begin(rng))
	>::type
	;
// 配列要素の型取得
#define rangetype(rng) decltype(_f_rangetype(rng))

const int v[] = {1, 2, 3, 4 ,5};
// {12.56f, 15.7f}になる?
auto q = from(v)
	>> where ([](const rangetype(v) &x) { return x > 3; });
	>> select([](const rangetype(v) &x) -> float { return x * 3.14f; })

	//>> where ([](const rangetype(v) &x) { return x > 12.0f; }) // NG.(12.56fがfalseになる)
	>> where([](const float &x) { return x > 12.0f; }) // ここだけ推論してない
	;

受け取ったクエリのfrontメソッドを使えばdecltypeで推論出来ますが、一貫性を持たせようとすると冗長且つめんどくさいです。

template <class Query>
auto _f_querytype(Query &&q) ->
	typename std::remove_const<
		typename std::remove_reference<
			decltype(q.front())
		>::type
	>::type
	;
// クエリの型取得
#define querytype(q) decltype(_f_querytype(q))

const int v[] = {1, 2, 3, 4 ,5};
// {12.56f, 15.7f}
auto q1 = from(v) >> where([](const rangetype(v) &x) { return x > 3; });
auto q2 = q1 >> select([](const querytype(q1) &x) -> float { return x * 3.14f; });
auto q3 = q2 >> where([](const querytype(q2) &x) { return x > 12.0f; });

なので、C++11ではラムダ式には推論はしない方が良いと思います、どうしても適用したい場合は上記の事に注意しながら実装するって事になるでしょう。

まとめ

C++でもLINQを使いたいって時には凄く役立つライブラリだと思いますがC++11時点ではまだ若干使いづらい面があります、

しかし、C++14になれば非常に使えるライブラリだと思います。

Connection: close