Weitere ähnliche Inhalte Ähnlich wie 中3女子が狂える本当に気持ちのいい constexpr (20) Kürzlich hochgeladen (10) 中3女子が狂える本当に気持ちのいい constexpr1. 中3女子が狂える
本当に
気持ちのいい
constexpr
Boost.勉強会 #8
bolero_MURAKAMI
2012/2/11
2. ◆⾃⼰紹介
• 名前 : 村上 原野 (むらかみ げんや)
@bolero_MURAKAMI, id:boleros
• 棲息地: ⼤都会岡⼭
• 仕事 : 猪⾵来美術館 陶芸指導員
・普段はやきものの修⾏をしたり、
縄⽂⼟器をつくったりしています
・趣味は constexpr です
4. ◆⾃⼰紹介
• 公開しているライブラリ:
Sprout C++ Library (constexpr ライブラリ)
github.com/bolero-MURAKAMI/Sprout
• 前回発表資料:
【中3⼥⼦でもわかる constexpr】
www.slideshare.net/GenyaMurakami
5. ◆アジェンダ
• はじめに
• constexpr おさらい
• constexpr と TMP の連携
• (続)constexpr レイトレーシング
• まとめ
6. ◆constexpr とは?
• constexpr 宣⾔された変数は、コンパイル時定数になる
constexpr int zero = 0; // constexpr 変数
using zero_t = std::integral_constant<int, zero>; // テンプレートにも渡せる
• constexpr 宣⾔された関数やコンストラクタは、コンパ
イル時にも実⾏時にも呼び出すことができる
constexpr int always_zero() { return 0; } // constexpr 関数
constexpr int compiletime_zero = always_zero(); // コンパイル時呼出
int runtime_zero = always_zero(); // 実行時呼出
• リテラル型のオブジェクトは、コンパイル時定数にでき
る
struct literal_type { }; // リテラル型のクラス
constexpr auto literal = literal_type{ }; // クラスインスタンスを定数式に
7. ◆constexpr とは?
• constexpr 関数の制限
template<class T> 関数テンプレートも
constexpr T square(T const& n) { constexpr 指定できる
static_assert( true, “” );
// static_assert( n != 0, “” );
typedef T t1;
using t2 = T;
using std::ptrdiff_t;
using namespace std;
return n * n;
}
– static_assert, typedef, using, 及び⾼々1つの return ⽂のみ
を書くことができる
– 引数は定数式にならない
8. ◆C++ プログラミングのレイヤー
C++03 [処理されるもの] [プログラマのすること]
プリプロセス時の世界 プリプロセッサ
(魔界) ソースコード
メタプログラミング
テンプレート
型
コンパイル時の世界 メタプログラミング
(ライブラリアンが多数棲息)
定数式
実⾏時の世界 実⾏時オブジェクト
(⼈間界)
通常の
プログラミング
9. ◆C++ プログラミングのレイヤー
C++11 [処理されるもの] [プログラマのすること]
プリプロセス時の世界 プリプロセッサ
(魔界) ソースコード
メタプログラミング
テンプレート
型
コンパイル時の世界 メタプログラミング
(ライブラリアンが多数棲息)
定数式
constexpr
実⾏時の世界 実⾏時オブジェクト
(⼈間界)
通常の
プログラミング
10. ◆C++ プログラミングのレイヤー
C++11 [処理されるもの] [プログラマのすること]
プリプロセス時の世界
連携したい
プリプロセッサ
(魔界) ソースコード
メタプログラミング
テンプレート
型
コンパイル時の世界 メタプログラミング
(ライブラリアンが多数棲息)
定数式
constexpr
実⾏時の世界 実⾏時オブジェクト
(⼈間界)
通常の
プログラミング
11. ◆Sprout C++ Library
• constexpr ⽂字列
• constexpr タプル
• constexpr バリアント
• constexpr アルゴリズム
• constexpr 範囲アルゴリズム
• constexpr コンテナ操作
• constexpr 乱数
• constexpr ハッシュ関数
• constexpr UUID
• constexpr 構⽂解析
• constexpr レイトレーシング
12. ◆Sprout C++ Library
• constexpr ⽂字列
#include <sprout/string.hpp>
#include <sprout/algorithm.hpp>
/* コンパイル時文字列 */
static constexpr auto hello = sprout::to_string("Hello,world!");
static_assert( hello == "Hello,world!", "" );
/* 文字列連結 */
static_assert( hello + "!!!" == "Hello,world!!!!", "" );
/* 文字列切り出し */
static_assert( hello.substr(0, 5) == "Hello", "" );
/* 文字列反転 */
static_assert( sprout::reverse(hello) == "!dlrow,olleH", "" );
13. ◆Sprout C++ Library
• constexpr ⽂字列
#include <sprout/string.hpp>
#include <sprout/algorithm.hpp>
/* コンパイル時文字列 */
static constexpr auto hello = sprout::to_string("Hello,world!");
static_assert( hello == "Hello,world!", "" );
/* 文字列連結 */
static_assert( hello + "!!!" == "Hello,world!!!!", "" );
/* 文字列切り出し */
static_assert( hello.substr(0, 5) == "Hello", "" );
/* 文字列反転 */
static_assert( sprout::reverse(hello) == "!dlrow,olleH", "" );
このように、constexpr で様々なクラスを
とてもわかりやすく簡単に扱うことができる。
14. ◆constexpr と TMP の連携
• クラステンプレートにコンパイル時定数
を渡すには
• index_tuple idiom
• 型⽂字列と constexpr ⽂字列の相互変換
16. ◆クラステンプレートにコンパイル時定数を渡すには
• 整数型を渡す 整数型はテンプレート引数に
template<int W, int H> RectArea { そのまま渡せる
static constexpr int value = W * H;
};
static constexpr int w = 50;
static constexpr int h = 100;
static_assert( RectArea<w, h>::value == 5000, “” )
24. ◆クラステンプレートにコンパイル時定数を渡すには
• ポインタをテンプレート引数にしてみる
template<Rect const* P>
struct Area {
static constexpr int value = P->w * P->h;
};
int main() { コンパイルエラー!!
static constexpr Rect rect = {10, 20};
static_assert( Area<&rect>::value == 200, “” );
}
• テンプレート引数に渡せる値
– 整数型 / リンケージを持つオブジェクト
• ローカル変数、⼀時オブジェクト、⽂字列リテラル等を
テンプレート引数に渡すことはできない
26. ◆クラステンプレートにコンパイル時定数を渡すには
• プロキシクラスをテンプレート引数にしてみる
template<class Proxy>
struct Area { プロキシを介して値を取得
static constexpr int value = Proxy()().w * Proxy()().h;
};
int main() { proxy クラスの operator() は
static constexpr Rect rect = {10, 20}; ローカル変数の Rect を返す
struct proxy {
constexpr Rect const& operator()() const { return rect; }
};
static_assert( Area<proxy>::value == 200, “” );
} OK!
• 短所
– プロキシクラスをいちいち定義しなければならない
28. ◆index_tuple idiom
• index_tuple と index_range ヘルパ
template<ptrdiff_t... Indexes>
struct index_tuple;
template<ptrdiff_t First, ptrdiff_t Last>
struct index_range;
static_assert( std::is_same<
index_range<0, 5>::type,
index_tuple<0, 1, 2, 3, 4>
>::value, “” );
29. ◆index_tuple idiom
• index_tuple と index_range ヘルパ
template<ptrdiff_t... Indexes> パラメータパックを
struct index_tuple; pack expansion expression
(Indexes...) で使う
template<ptrdiff_t First, ptrdiff_t Last>
struct index_range;
(First .. Last] の
index_tuple を⽣成する
static_assert( std::is_same<
ヘルパメタ関数
index_range<0, 5>::type,
index_tuple<0, 1, 2, 3, 4>
>::value, “” );
30. ◆index_tuple idiom
• TMP で index_tuple を使う
#include <sprout/index_tuple.hpp>
#include <boost/mpl/vector.hpp>
template<class Seq, class IndexTuple>
struct to_tuple_impl;
template<class Seq, ptrdiff_t... Indexes>
struct to_tuple_impl<Seq, index_tuple<Indexes...> > {
typedef tuple< mpl::at_c<Seq, Indexes>... > type;
};
/* MPLシーケンスから tuple への変換 */
template<class Seq>
struct to_tuple : to_tuple_impl<
Seq,
typename index_range<0, mpl::size<Seq>::value>::type
> { };
typedef mpl::vector<int, double> vec;
static_assert( is_same<to_tuple<vec>::type, tuple<int, double> >::value, “” );
31. ◆index_tuple idiom
• TMP で index_tuple を使う
#include <sprout/index_tuple.hpp>
#include <boost/mpl/vector.hpp>
index_tuple<Indexes...> で
template<class Seq, class IndexTuple> テンプレート引数を特殊化する
struct to_tuple_impl;
template<class Seq, ptrdiff_t... Indexes>
struct to_tuple_impl<Seq, index_tuple<Indexes...> > {
typedef tuple< mpl::at_c<Seq, Indexes>... > type;
};
/* MPLシーケンスから tuple への変換 */ pack expansion expression で
template<class Seq> Indexes を使ったリストに展開する
struct to_tuple : to_tuple_impl<
Seq,
typename index_range<0, mpl::size<Seq>::value>::type
> { };
index_range で index_tuple を
⽣成して実装に丸投げ
typedef mpl::vector<int, double> vec;
static_assert( is_same<to_tuple<vec>::type, tuple<int, double> >::value, “” );
32. ◆index_tuple idiom
• constexpr 関数で index_tuple を使う
#include <sprout/index_tuple.hpp>
#include <sprout/array.hpp>
template<class T, size_t N, ptrdiff_t...Indexes>
constexpr array<T, N> to_array_impl(T const (& arr)[N], index_tuple<Indexes...>) {
return array<T, N>{{ arr[Indexes]... }};
}
/* 生配列から sprout::array への変換 */
template<class T, size_t N>
constexpr array<T, N> to_array(T const (& arr)[N]) {
return to_array_impl(arr, typename index_range<0, N>::type());
}
static constexpr int arr[2] = { 1, 2 };
static constexpr array<int, 2> s = to_array(arr);
33. ◆index_tuple idiom
• constexpr 関数で index_tuple を使う
#include <sprout/index_tuple.hpp> index_tuple<Indexes...>
#include <sprout/array.hpp> を引数にして推論させる
template<class T, size_t N, ptrdiff_t...Indexes>
constexpr array<T, N> to_array_impl(T const (& arr)[N], index_tuple<Indexes...>) {
return array<T, N>{{ arr[Indexes]... }};
}
pack expansion expression で
/* 生配列から sprout::array への変換 */
Indexes を使ったリストに展開する
template<class T, size_t N>
constexpr array<T, N> to_array(T const (& arr)[N]) {
return to_array_impl(arr, typename index_range<0, N>::type());
}
index_range で index_tuple を
static constexpr int arr[2] = { 1, 2 }; ⽣成して実装に丸投げ
static constexpr array<int, 2> s = to_array(arr);
34. ◆index_tuple idiom
• index_tuple イディオムは、インデックスアクセス可能
なデータ構造⼀般に適⽤できる
– 配列
– タプル
– ランダムアクセスイテレータ
– 型リスト
• constexpr に限らず、通常の関数や TMP にも同じよう
に応⽤できる
• ただしインデックスアクセス不可なデータ構造には適⽤
できない
– ⾮ランダムアクセスなイテレータなど
35. ◆型⽂字列と constexpr ⽂字列の相互変換
• 型⽂字列
typedef mpl::string< ‘Hell’, ‘o,wo’, ‘rld!’ > hello_t;
– ⽂字列は可変⻑テンプレート引数で表現される
– ⽂字列リテラルは使えない
• (マクロの⿊魔術を使えば制限付きで可能ではある)
• constexpr ⽂字列
static constexpr auto hello = sprout::to_string( “Hello,world!” );
– コンパイル時定数である
– ⽂字列リテラルが使⽤可能
• これらを相互に変換できるようにしたい
36. ◆型⽂字列と constexpr ⽂字列の相互変換
• mpl::string → sprout::string
#include <type_traits>
#include <sprout/index_tuple.hpp>
#include <sprout/type.hpp>
#include <sprout/string.hpp>
#include <sprout/type/boost/mpl/string.hpp>
using namespace std;
using namespace sprout;
namespace mpl = boost::mpl;
namespace types = sprout::types;
37. ◆型⽂字列と constexpr ⽂字列の相互変換
• mpl::string → sprout::string
/* T::value がナル文字であるか返すメタ関数クラス */
struct is_nul {
template<class T, class = void>
struct apply : false_type { };
template<class T>
struct apply<T, typename enable_if<T::value == 0>::type>
: true_type
{ };
};
/* ナル文字までの文字列長 */
template<class Seq>
struct str_length : types::distance<
typename types::begin<Seq>::type,
typename types::seq::find_if<Seq, is_nul>::type
> { };
38. ◆型⽂字列と constexpr ⽂字列の相互変換
• mpl::string → sprout::string
template<class Seq, ptrdiff_t... Indexes>
constexpr sprout::basic_string<
typename Seq::value_type,
str_length<Seq>::value
> to_sprout_string_impl( index_tuple<Indexes...> ) {
return sprout::make_string_as<typename Seq::value_type>(
types::tuple_element<Indexes, Seq>::type::value...
);
}
/* sprout::string に変換 */
template<class Seq>
constexpr sprout::basic_string<
typename Seq::value_type,
str_length<Seq>::value
> to_sprout_string() {
return to_sprout_string_impl<Seq>(
typename index_range<0, str_length<Seq>::value>::type()
);
}
39. ◆型⽂字列と constexpr ⽂字列の相互変換
• mpl::string → sprout::string
template<class Seq, ptrdiff_t... Indexes>
constexpr sprout::basic_string<
index_tuple<Indexes...> を
typename Seq::value_type,
実装関数で受け取る
str_length<Seq>::value
> to_sprout_string_impl( index_tuple<Indexes...> ) {
return sprout::make_string_as<typename Seq::value_type>(
types::tuple_element<Indexes, Seq>::type::value...
); Indexes を使ってシーケンス要素の
} ⽂字のリストに展開
/* sprout::string に変換 */
template<class Seq>
constexpr sprout::basic_string< basic_string<Elem, Length>
typename Seq::value_type,
str_length<Seq>::value index_range で index_tuple を
> to_sprout_string() { ⽣成して実装に丸投げ
return to_sprout_string_impl<Seq>(
typename index_range<0, str_length<Seq>::value>::type()
);
}
40. ◆型⽂字列と constexpr ⽂字列の相互変換
• mpl::string → sprout::string
typedef mpl::string< ‘foo’, ‘bar’ > type;
static constexpr auto s = to_sprout_string<type>();
static_assert( s == “foobar”, “” );
mpl::string (型)から
sprout::string (コンパイル時定数)へ変換
• ⼀般に【型→値】の変換は⾃明に書ける(場合が多い)
41. ◆型⽂字列と constexpr ⽂字列の相互変換
• sprout::string → mpl::string
#include <type_traits>
#include <sprout/index_tuple.hpp>
#include <sprout/fixed_container.hpp>
#include <sprout/string.hpp>
#include <boost/mpl/string.hpp>
#include <boost/preprocessor/cat.hpp>
using namespace std;
using namespace sprout;
namespace mpl = boost::mpl;
42. ◆型⽂字列と constexpr ⽂字列の相互変換
• sprout::string → mpl::string
template<class Proxy>
struct to_mpl_string {
template<class IndexTuple>
struct impl;
template<ptrdiff_t... Indexes>
struct impl<index_tuple<Indexes...> > {
typedef mpl::string<
sprout::begin(Proxy()())[Indexes]...
> type;
};
/* mpl::string に変換 */
typedef class impl<
typename index_range<0, sprout::size(Proxy()())>::type
>::type type;
};
43. ◆型⽂字列と constexpr ⽂字列の相互変換
• sprout::string → mpl::string
template<class Proxy> index_tuple<Indexes...> を
struct to_mpl_string { 実装メタ関数で受け取る
template<class IndexTuple>
struct impl;
template<ptrdiff_t... Indexes>
struct impl<index_tuple<Indexes...> > {
typedef mpl::string< mpl::string<Elem...>
sprout::begin(Proxy()())[Indexes]...
> type;
}; Indexes を使ってシーケンス要素の
/* mpl::string に変換 */ ⽂字のリストに展開
typedef class impl<
typename index_range<0, sprout::size(Proxy()())>::type
>::type type;
};
index_range で index_tuple を
⽣成して実装に丸投げ
44. ◆型⽂字列と constexpr ⽂字列の相互変換
• sprout::string → mpl::string
/* 変換結果を typedef するマクロ*/
#define STRING_CLASS_TYPEDEF(SOURCE, TYPE) ¥
struct BOOST_PP_CAT(PROXY_, __LINE__) { ¥
constexpr typename remove_reference<decltype(SOURCE)>::type ¥
operator() () const { return SOURCE; } ¥
}; ¥
typedef typename to_mpl_string< ¥
BOOST_PP_CAT(PROXY_, __LINE__) ¥
>::type TYPE
45. ◆型⽂字列と constexpr ⽂字列の相互変換
• sprout::string → mpl::string
PROXY_127 のような名前の
プロキシクラスが定義される
/* 変換結果を typedef するマクロ*/
#define STRING_CLASS_TYPEDEF(SOURCE, TYPE) ¥
struct BOOST_PP_CAT(PROXY_, __LINE__) { ¥
constexpr typename remove_reference<decltype(SOURCE)>::type ¥
operator() () const { return SOURCE; } ¥
}; ¥ operator() は変換元ソースを
typedef typename to_mpl_string< ¥ そのまま返す constexpr 関数
BOOST_PP_CAT(PROXY_, __LINE__) ¥
>::type TYPE
定義したプロキシクラスを渡す
46. ◆型⽂字列と constexpr ⽂字列の相互変換
• sprout::string → mpl::string
#include <boost/mpl/equal.hpp> sprout::string (コンパイル時定数)から
mpl::string (型)へ変換
static constexpr auto s = sprout::to_string( “foobar” );
STRING_CLASS_TYPEDEF(s, type);
static_assert( mpl::equal< type, mpl::string<'foo', 'bar'> >::value, “” );
• 【値→型】の変換は⼯夫次第で可能
• ⽋点
– ⾃明な書き⽅ができない(場合が多い)
– プロキシクラスの定義が必要な場合、インラインに書けないし、
constexpr 関数の中で使えない
47. ◆constexpr と TMP の連携
• 整数型以外のコンパイル時定数でも、テンプ
レートに受け渡す⽅法はある
• index_tuple イディオムや、プロキシクラス等
を活⽤することで、constexpr と TMP の間で
相互にやりとりをすることができる
53. ◆(続)constexpr レイトレーシング
• エレメント(カラーやスペキュラ)を表現するには
template<typename Element, typename Scale>
class plaid_element {
private:
Element elem1_;
Element elem2_;
Scale scale_;
template<typename Unit>
constexpr Element calc_1(Unit const& u, Unit const& v) const {
return (u >= 0 && v >= 0) || (u < 0 && v < 0)
? elem1_ : elem2_;
}
public:
template<typename Unit>
constexpr Element operator() (Unit const& u, Unit const& v) const {
return calc_1(
(u < 0 ? scale_ + fmod(u, scale_) : fmod(u, scale_)) - scale_ / 2,
(v < 0 ? scale_ + fmod(v, scale_) : fmod(v, scale_)) - scale_ / 2
);
}
};
54. ◆(続)constexpr レイトレーシング
• エレメント(カラーやスペキュラ)を表現するには
template<typename Element, typename Scale>
class plaid_element {
private: 市松模様を表現するエレメント
Element elem1_;
Element elem2_; 座標を⼆つに分けて
Scale scale_; どちらかのエレメント(⽩/⿊など)を返す
template<typename Unit>
constexpr Element calc_1(Unit const& u, Unit const& v) const {
return (u >= 0 && v >= 0) || (u < 0 && v < 0)
? elem1_ : elem2_;
エレメントのコンセプトは
}
uv 座標を受け取って結果を返す
public:
operator()(u, v) を持つこと
template<typename Unit>
constexpr Element operator() (Unit const& u, Unit const& v) const {
return calc_1(
(u < 0 ? scale_ + fmod(u, scale_) : fmod(u, scale_)) - scale_ / 2,
(v < 0 ? scale_ + fmod(v, scale_) : fmod(v, scale_)) - scale_ / 2
);
}
};
55. ◆(続)constexpr レイトレーシング
• テクスチャマップを読み込むには
#include <sprout/darkroom.hpp>
/* ファイル "earth.tex.hpp" の内容を tex に読み込む */
#define DARKROOM_DEF_LOAD_TEXTURE_IDENTIFIER tex
#define DARKROOM_DEF_LOAD_TEXTURE_FILE "earth.tex.hpp"
#include DARKROOM_LOAD_TEXTURE
– earth.png
56. ◆(続)constexpr レイトレーシング
• テクスチャマップを読み込むには
#include <sprout/darkroom.hpp>
/* ファイル "earth.tex.hpp" の内容を tex に読み込む */
#define DARKROOM_DEF_LOAD_TEXTURE_IDENTIFIER tex
#define DARKROOM_DEF_LOAD_TEXTURE_FILE "earth.tex.hpp"
#include DARKROOM_LOAD_TEXTURE
– 擬似コード
/* 実装は include ディレクティブで CVS を取りこむトリックと同じような感じ */
constexpr auto tex {
# include "earth.tex.hpp"
};
“earth.tex.hpp” の中⾝は
コンマ区切りのピクセルデータのようなもの