Successfully reported this slideshow.
Your SlideShare is downloading. ×

中3女子でもわかる constexpr

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad

Check these out next

1 of 100 Ad
Advertisement

More Related Content

Slideshows for you (20)

Similar to 中3女子でもわかる constexpr (20)

Advertisement

Recently uploaded (20)

中3女子でもわかる constexpr

  1. 1. 中3女子でもわかる! constexpr Boost.勉強会 #7 bolero_MURAKAMI 2011/12/3
  2. 2. ◆⾃⼰紹介 • 名前 : 村上 原野 (むらかみ げんや) @bolero_MURAKAMI, id:boleros • 棲息地: ⼤都会岡⼭ • 仕事 : 猪⾵来美術館陶芸指導員 ・普段はやきものの修⾏をしたり、 縄⽂⼟器をつくったりしています ・趣味は constexpr です
  3. 3. ◆⾃⼰紹介 • 好きな C++11 の機能: constexpr • 嫌いな C++11 のキーワード: constexpr • 次期 C++ で強化されてほしい機能: constexpr • 次期 C++ で消えてほしいキーワード: constexpr
  4. 4. ◆⾃⼰紹介 • 公開しているライブラリ: Sprout C++ Library (constexpr ライブラリ) github.com/bolero-MURAKAMI/Sprout
  5. 5. ◆アジェンダ • はじめに • constexpr とは? • constexpr 実装技法 • constexpr の3⼤コスト • constexpr ライブラリを使ってみる • まとめ
  6. 6. ◆constexpr とは? • N3290 7.1.5 The constexpr specifier [dcl.constexpr] 1 The constexpr specifier shall be applied only to the definition of a variable, the declaration of a function or function template, or the declaration of a static data member of a literal type (3.9). If any declaration of a function or function template has constexpr specifier, then all its declarations shall contain the constexpr specifier. [ Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier. -end note ] [ Note: Function parameters cannot be declared constexpr.-end note ]
  7. 7. ◆constexpr とは? • N3290 7.1.5 constexpr 指定子 [dcl.constexpr] constexpr 指定子は、変数の定義、関数または関数テンプレートの宣言、 またはリテラル型(3.9)の静的データメンバの宣言に適用されるものとする。 (中略) [注:関数のパラメータは constexpr 宣言することはできない。] • C++11 で導⼊されたキーワードである • decl-specifier に分類される – (型修飾⼦ではない)
  8. 8. ◆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{ }; // クラスインスタンスを定数式に
  9. 9. ◆constexpr ⼊⾨ • プログラミングの魔導書vol.2 の江添さん の記事を読んでください • 読んでなかったら買ってください
  10. 10. ◆C++ プログラミングのレイヤー C++03 [処理されるもの] [プログラマのすること] プリプロセス時の世界 プリプロセッサ (魔界) ソースコード メタプログラミング テンプレート 型 コンパイル時の世界 メタプログラミング (ライブラリアンが多数棲息) 定数式 実⾏時の世界 実⾏時オブジェクト (⼈間界) 通常の プログラミング
  11. 11. ◆C++ プログラミングのレイヤー C++03 [処理されるもの] [プログラマのすること] プリプロセス時の世界 プリプロセッサ (魔界) ソースコード メタプログラミング テンプレート 型 コンパイル時の世界 メタプログラミング (ライブラリアンが多数棲息) 定数式 どっちも「値」を 求めるのは同じでも…… わたしたち 離ればなれね…… 実⾏時の世界 実⾏時オブジェクト (⼈間界) 通常の プログラミング
  12. 12. ◆C++ プログラミングのレイヤー C++11 [処理されるもの] [プログラマのすること] プリプロセス時の世界 プリプロセッサ (魔界) ソースコード メタプログラミング テンプレート それ constexpr 型 で出来るよ! コンパイル時の世界 メタプログラミング (ライブラリアンが多数棲息) 定数式 抱いて!! constexpr キャーカッコイー!! 実⾏時の世界 実⾏時オブジェクト (⼈間界) 通常の プログラミング
  13. 13. ◆定数も⾮定数も constexpr で • TMP で定数値を計算する typedef typename mpl::max<A, B>::type value_t; constexpr auto compiletime_value = value_t::value; • 関数で実⾏時に値を計算する auto runtime_value = std::max(a, b);
  14. 14. ◆定数も⾮定数も constexpr で • TMP で定数値を計算する typedef typename mpl::max<A, B>::type value_t; constexpr auto compiletime_value = value_t::value; • 関数で実⾏時に値を計算する auto runtime_value = std::max(a, b); ↓ • どっちも constexpr で出来るよ! template<class T> // constexpr 関数テンプレート constexpr T max(T const& a, T const& b) { return a < b ? b : a; } auto runtime_value = max(a, b); しかも TMP より⾼速 constexpr auto compiletime_value = max(a, b);
  15. 15. ◆コンパイル時⽂字列 • TMP でやってみる typedef mpl::string<’Hell’, ’o, wo’, ’rld! ’> hello_t; – 書きづらい(醜い) – 遅い – 制限が多い – 型の領域だけでは無理がある
  16. 16. ◆コンパイル時⽂字列 • TMP でやってみる typedef mpl::string<’Hell’, ’o, wo’, ’rld! ’> hello_t; – 書きづらい(醜い) – 遅い – 制限が多い – 型の領域だけでは無理がある ↓ Sprout C++ Library の • 簡単さ。そう、constexpr ならね constexpr string #include <sprout/string.hpp> constexpr auto hello = sprout::to_string(“hello, world!”); ⽂字列リテラルも そのまま使える
  17. 17. ◆コンパイル時⽂字列 • こんなことも出来るよ! – 例) コンパイル時⽂字列解析 (Boost.Spirit.Qi ⾵) { using namespace sprout; using namespace sprout::weed; constexpr auto s = to_string("{550E8400-E29B-41D4-A716-446655440000}"); constexpr auto unbracket_uuid_p = repeat[lim<16>(hex8f)] | repeat[lim<4>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<6>(hex8f)]; constexpr auto uuid_p = (unbracket_uuid_p | '{' >> unbracket_uuid_p >> '}') >> eoi; constexpr auto parsed = parse(s.begin(), s.end(), uuid_p); std::cout << std::boolalpha << realign_to<uuid>(parsed.attr()) << "¥n"; }
  18. 18. ◆コンパイル時⽂字列 • こんなことも出来るよ! – 例) コンパイル時⽂字列解析 (Boost.Spirit.Qi ⾵) { using namespace sprout; UUID ⽂字列 using namespace sprout::weed; constexpr auto s = to_string("{550E8400-E29B-41D4-A716-446655440000}"); constexpr auto unbracket_uuid_p ブラケット無しの = repeat[lim<16>(hex8f)] UUID にマッチするパーサ | repeat[lim<4>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<6>(hex8f)]; constexpr auto uuid_p ブラケット無し/有り両⽅に = (unbracket_uuid_p | '{' >> unbracket_uuid_p >> '}') >> eoi; マッチするようパーサを合成 constexpr auto parsed = parse(s.begin(), s.end(), uuid_p); パースを実⾏ std::cout << std::boolalpha << realign_to<uuid>(parsed.attr()) << "¥n"; } 最後以外全部コンパイル時に 処理される
  19. 19. ◆constexpr で扱えるデータ [リテラル型] [スカラ型] [リテラル型の配列] [算術型] LiteralType [N] [整数型] int, unsigned int, char, ... [リテラル型への参照] [浮動⼩数点型] float, double, ... LiteralType const& [ポインタ型] [ポインタ] int const*, int (*)(void), ... 特定の条件を満たす [メンバポインタ] int T::*, int (T::*)(void), ... ユーザ定義クラス [列挙型] enum
  20. 20. ◆リテラル型クラスの条件 • コンパイラの要求 – trivial コピーコンストラクタを持つ – ⾮ trivial ムーブコンストラクタを持たない – trivial デストラクタを持つ – trivial デフォルトコンストラクタか、コピーでもムーブでもな い constexpr コンストラクタを持つ – ⾮ static データメンバと基本クラスは、全てリテラル型である
  21. 21. ◆リテラル型クラスの条件 • プログラマの「べからず」 – 仮想関数や仮想基底クラスを書かない – ユーザ定義コピーコンストラクタを書かない • (delete もしない) – ユーザ定義ムーブコンストラクタを書かない • (delete するのはよい) – ユーザ定義デストラクタを書かない – ユーザ定義デフォルトコンストラクタを書くなら原則 constexpr にする • (デフォルトコンストラクタが trivial でも constexpr でもない場 合、別の constexpr コンストラクタを書く) – リテラル型以外の⾮ static データメンバや基底クラスを使わな い
  22. 22. ◆リテラル型クラスの条件 • リテラル型になれない例 struct NonLiteralType 仮想基底クラスは NG! : virtual BaseType : NonLiteralBaseType リテラル型以外の基底クラスは NG! { }; struct NonLiteralType { virtual MemberFunction() const; 仮想関数は NG! }; ユーザ定義コピーコンストラクタは NG! struct NonLiteralType { constexpr LiteralType(LiteralType const&) { } LiteralType(LiteralType const&) = delete; }; コピーコンストラクタの delete も NG!
  23. 23. ◆リテラル型クラスの条件 • リテラル型になれない例 struct NonLiteralType { ユーザ定義デストラクタは NG! ~LiteralType() { } }; デフォルトコンストラクタが⾮ constexpr の場合 struct NonLiteralType { 別の constexpr コンストラクタがあれば OK LiteralType() { } // constexpr explicit LiteralType(int) { } }; ユーザ定義ムーヴコンストラクタは NG! struct NonLiteralType { constexpr LiteralType(LiteralType&&) { } // LiteralType(LiteralType&&) = delete; }; ムーヴコンストラクタの delete は OK
  24. 24. ◆リテラル型クラスの条件 • リテラル型になれない例 struct NonLiteralType { private: リテラル型以外のデータメンバは NG! NonLiteralDataType data_member_; };
  25. 25. ◆関数を constexpr 化する • 条件分岐 • ループ • ローカル変数 • エラー通知 • シグネチャの constexpr 化 • 配列操作 (Variadic function) • 配列操作 (index_tuple イディオム)
  26. 26. ◆条件分岐 • ⾮ constexpr 関数 template<class T> T max(T const& a, T const& b) { if (a < b) return b; else return a; } if ⽂ ↓ • constexpr 関数 条件演算⼦ template<class T> constexpr T max(T const& a, T const& b) { return (a < b) ? b : a; }
  27. 27. ◆ループ • ⾮ constexpr 関数 template<class Iter, class T> Iter find(Iter first, Iter last, T const& val) { while (first != last) { if (*first == val) return first; ++first; } return first; ループ構⽂ } ↓ 再帰 • constexpr 関数 template<class Iter, class T> constexpr Iter find(Iter first, Iter last, T const& val) { return (first != last) ? (*first == val) ? first : find(first + 1, last, val) : first; ++演算⼦は使えない } (副作⽤があるから)
  28. 28. ◆ループ • ⾮ constexpr 関数 template<class Iter, class T> Iter find(Iter first, Iter last, T const& val) { while (first != last) { if (*first == val) return first; ++first; } return first; ループ構⽂ } ↓ 再帰 • constexpr 関数 template<class Iter, class T> constexpr Iter find(Iter first, Iter last, T const& val) { return (first == last) || (*first == val) ? first : find(first + 1, last, val); } 式を整理して このようにも書ける
  29. 29. ◆ローカル変数 • ⾮ constexpr 関数 double heron(double a, double b, double c) { double s = (a+b+c)/2; return std::sqrt(s*(s-a)*(s-b)*(s-c)); ローカル変数 } ↓ 実装⽤関数の引数に • constexpr 関数 constexpr double heron_impl(double a, double b, double c, double s) { return std::sqrt(s*(s-a)*(s-b)*(s-c)); } constexpr double heron(double a, double b, double c) { return heron_impl(a, b, c, (a+b+c)/2); } 実装⽤関数 ※ constexpr std::sqrt は libstdc++ の⾮標準拡張
  30. 30. ◆エラー通知 • ⾮ constexpr 関数 template<class T> T const* next(T const* p) { if (p) return p + 1; else assert(0); // error } assert / 実⾏時例外 ↓ • constexpr 関数 例外 template<class T> constexpr T const* next(T const* p) { return p ? p + 1 : throw std::invalid_argument("p is nullptr"); } コンパイル時にはコンパイルエラー 実⾏時には例外が投げられる
  31. 31. ◆エラー通知 • ⾮ constexpr 関数 template<class T> T const* next(T const* p) { if (p) return p + 1; else assert(0); // error } assert / 実⾏時例外 ↓ • constexpr 関数 × static_assert template<class T> constexpr T const* next(T const* p) { static_assert(p != 0, "p is nullptr"); // NG! return p + 1; } p が実⾏時引数のとき p !=0 は⾮定数式
  32. 32. ◆シグネチャの constexpr 化 • ⾮ constexpr 関数 template<class T> void inc(T& x) { ++x; } 返値 void → 値を返す • constexpr 関数 引数書換え → 副作⽤無しに template<class T> constexpr T inc(T const& x) { return x + 1; }
  33. 33. ◆シグネチャの constexpr 化 • ⾮ constexpr 関数 template<class Iter, class Expr, class Attr> bool parse(Iter& first, Iter last, Expr const& expr, Attr& attr); 複数の結果(副作⽤)がある ↓ • constexpr 関数 タプルにして返す template<class Attr, class Iter, class Expr> constexpr tuple<bool, Iter, Attr> parse(Iter first, Iter last, Expr const& expr);
  34. 34. ◆配列操作 (Variadic function) • reverse アルゴリズムを実装してみる #include <sprout/array.hpp> constexpr array using namespace sprout; template<class T, size_t N> constexpr array<T, N> reverse(array<T, N> const& arr); 適⽤結果の配列を返す
  35. 35. ◆配列操作 (Variadic function) • reverse アルゴリズムを実装してみる template<class T, size_t N, class... Args> constexpr array<T, N> reverse_impl(array<T, N> const& arr, Args const&... args) { return (sizeof...(Args) >= N) 要素全て Parameter pack に追加されるまで ? array<T, N>{{ args... }} : reverse_impl(arr, args..., arr[N-1-sizeof...(Args)]); } 配列要素のケツから次々 Parameter pack の末尾に挿⼊ template<class T, size_t N> constexpr array<T, N> reverse(array<T, N> const& arr) { return reverse_impl(arr); }
  36. 36. ◆配列操作 (Variadic function) • reverse アルゴリズムを実装してみる template<class T, size_t N, class... Args> constexpr array<T, N> reverse_impl(array<T, N> const& arr, Args const&... args) { return (sizeof...(Args) >= N) ? array<T, N>{{ args... }} : reverse_impl(arr, args..., arr[N-1-sizeof...(Args)]); } template<class T, size_t N> constexpr array<T, N> reverse(array<T, N> const& arr) { return reverse_impl(arr); } • 残念ながら、このコードはコンパイルできない error: template instantiation depth exceeds maximum of 1024 (use -ftemplate-depth= to increase the maximum) テンプレート実体化の深さ限界を超えてしまった
  37. 37. ◆配列操作 (Variadic function) • reverse アルゴリズムを実装してみる template<class T, size_t N, class... Args> constexpr array<T, N> reverse_impl(array<T, N> const& arr, Args const&... args) { return (sizeof...(Args) >= N) sizeof...(Args) == N のとき再帰終了する? ? array<T, N>{{ args... }} : reverse_impl(arr, args..., arr[N-1-sizeof...(Args)]); } 「評価」はされないが 「テンプレートの実体化」はされる template<class T, size_t N> → 実体化の再帰は終了しない constexpr array<T, N> reverse(array<T, N> const& arr) { return reverse_impl(arr); } 結果、テンプレートの実体化が 無限再帰になる
  38. 38. ◆配列操作 (Variadic function) • reverse アルゴリズムを実装してみる template<class T, size_t N, class... Args> constexpr typename enable_if< (sizeof...(Args) >= N), SFINAE: ここで再帰終了 array<T, N> >::type reverse_impl(array<T, N> const& arr, Args const&... args) { return array<T, N>{{ args... }}; } 再帰条件を template<class T, size_t N, class... Args> SFINAE で書く constexpr typename enable_if< (sizeof...(Args) < N), array<T, N> SFINAE: 再帰を続ける場合 >::type reverse_impl(array<T, N> const& arr, Args const&... args) { return reverse_impl(arr, args..., arr[N-1-sizeof...(Args)]); } template<class T, size_t N> constexpr array<T, N> reverse(array<T, N> const& arr) { return reverse_impl(arr); }
  39. 39. ◆配列操作 (Variadic function) • Variadic function の再帰はテンプレート実体化の無限 再帰になりやすいので注意する • そうなるケースでは再帰条件を SFINAE にする • (static if ほしいです)
  40. 40. ◆配列操作 (index_tuple idiom) • また reverse アルゴリズムを実装してみる #include <sprout/array.hpp> constexpr array using namespace sprout; template<class T, size_t N> constexpr array<T, N> reverse(array<T, N> const& arr); 適⽤結果の配列を返す
  41. 41. ◆配列操作 (index_tuple idiom) • また reverse アルゴリズムを実装してみる #include <sprout/index_tuple.hpp> for index_tuple, index_range template<class T, size_t N, ptrdiff_t... Indexes> constexpr array<T, N> reverse_impl(array<T, N> const& arr, index_tuple<Indexes...>) { return array<T, N>{{ arr[N-1-Indexes]... }}; } template<class T, size_t N> constexpr array<T, N> reverse(array<T, N> const& arr) { return reverse_impl(arr, typename index_range<0, N>::type()); }
  42. 42. ◆配列操作 (index_tuple idiom) • また reverse アルゴリズムを実装してみる #include <sprout/index_tuple.hpp> template<class T, size_t N, ptrdiff_t... Indexes> 0..N-1 に推論され constexpr array<T, N> る reverse_impl(array<T, N> const& arr, index_tuple<Indexes...>) { return array<T, N>{{ arr[N-1-Indexes]... }}; } Pack expansion expression によって arr[N-1]..arr[0] に展開される template<class T, size_t N> constexpr array<T, N> reverse(array<T, N> const& arr) { return reverse_impl(arr, typename index_range<0, N>::type()); } index_range<0, N>::type は index_tuple<0..N-1> を返す
  43. 43. ◆配列操作 (index_tuple idiom) • index_tuple イディオムは、インデックスアクセス可能 なデータ構造⼀般に適⽤できる – 配列 – タプル – ランダムアクセスイテレータ – 型リスト • constexpr に限らず、通常の関数や TMP にも同じよう に応⽤できる • ただしインデックスアクセス不可なデータ構造には適⽤ できない – ⾮ランダムアクセスなイテレータなど
  44. 44. ◆クラスを constexpr 化する • コンストラクタで処理を委譲 • 状態を書き換えるメンバ関数の constexpr 化
  45. 45. ◆コンストラクタで処理を委譲 • Delegating constructor #include <sscrisk/cel/algorithm.hpp> constexpr minmax_element using namespace sscrisk::cel; template<class T> あるイテレータ範囲の min と max を struct minmax_t { 保持するクラス template<class Iter> constexpr minmax_t(Iter first, Iter last); private: constexpr コンストラクタでは T min_, max_; 初期化⼦リストにしか }; 処理を書けない
  46. 46. ◆コンストラクタで処理を委譲 • Delegating constructor template<class T> struct minmax_t { まず minmax を求めてから template<class Iter> 別のコンストラクタに処理を丸投げ constexpr minmax_t(Iter first, Iter last) : minmax_t(minmax_element(first, last)) {} private: template<class Iter> constexpr minmax_t(pair<Iter, Iter> const& minmax) : min_(*minmax.first) , max_(*minmax.second) minmax を min と max に振り分け {} T min_, max_; };
  47. 47. ◆コンストラクタで処理を委譲 • C++03 Delegating constructor workaround template<class T> struct minmax_t_impl { protected: template<class Iter> constexpr minmax_t_impl(pair<Iter, Iter> const& minmax) : min_(*minmax.first) , max_(*minmax.second) 実装⽤クラスで minmax を {} min と max に振り分け T min_, max_; }; private 継承 template<class T> struct minmax_t : private minmax_t_impl<T> { template<class Iter> 実装⽤クラスに処理を丸投げ constexpr minmax_t(Iter first, Iter last) : minmax_t_impl<T>(minmax_element(first, last)) {} private: using minmax_t_impl<T>::min_; using minmax_t_impl<T>::max_; }; 実装⽤クラスのメンバを using
  48. 48. ◆状態を書き換えるメンバ関数 • ⾮ constexpr クラス struct sha1 { void process_byte(unsigned char byte); template<class Iter> 状態を書き換える void process_block(Iter first, Iter last); ↓ }; 「次の状態」を返すように • constexprクラス struct sha1 { constexpr sha1 process_byte(unsigned char byte) const; template<class Iter> constexpr sha1 process_block(Iter first, Iter last) const; };
  49. 49. ◆状態を書き換えるメンバ関数 • ⾮ constexpr クラス struct random_generator { unsigned int operator()(); 状態を書き換え、 }; かつ処理結果を返す ↓ 処理結果と「次の状態」の タプルを返す • constexprクラス struct random_generator { constexpr pair<unsigned int, random_generator> operator()() const; };
  50. 50. ◆constexpr の3⼤コスト • オブジェクトコピーのコスト • 再帰とテンプレートインスタンス化のコ スト • コーディング上のコスト (⼈間のコスト)
  51. 51. ◆オブジェクトコピーのコスト • 配列の2つの要素の swap を考えてみる – ⾮ constexpr の場合 (元の配列を書換え) ⾼々1回のコピーと 2回の代⼊ (またはムーヴ) – constexpr の場合 (別の配列をつくる) 常に全要素の コピー • 単なる要素の交換に線形時間 O(n) を要する
  52. 52. ◆オブジェクトコピーのコスト • 状態を書き換えるメンバ関数の呼出しを考えてみる constexpr array<unsigned char, N> src = { /*...*/ }; sha1.process_byte(src[0]) .process_byte(src[1]) 計算量= /*...*/ (計算毎+コピー毎)×要素数 .process_byte(src[N-1]); sha1.process_block(src.begin(), src.end()); 計算量=計算毎×要素数+コピー • 状態を書き換えるメンバ関数を constexpr にすると、 (オブジェクトコピー×呼出し回数)の計算量が余計に かかる
  53. 53. ◆オブジェクトコピーのコスト • [回避するには] • 状態を変更する呼出しは可能な限り少なくする – (1つの関数で済ませる) • Variadic function や index_tuple イディオムを活⽤し てオブジェクトコピーが⾏われない処理を書く
  54. 54. ◆再帰とテンプレート実体化のコスト • 再帰を途中で打ち切るアルゴリズムを考えてみる template<class T, size_t N, class... Args> constexpr typename enable_if< SFINAE の番兵 (sizeof...(Args) >= N), array<T, N> >::type copy_to_find(array<T, N> const& arr, T const& val, Args const&... args) { return array<T, N>{{ args... }}; } template<class T, size_t N, class... Args> 引数 val と等しい要素があったら constexpr typename enable_if< そこまでを新しい配列にコピーする (sizeof...(Args) < N), array<T, N> >::type copy_to_find(array<T, N> const& arr, T const& val, Args const&... args) { return val == arr[sizeof...(Args)] ? array<T, N>{{ args... }} : find_copy(arr, val, args..., arr[sizeof...(Args)]); }
  55. 55. ◆再帰とテンプレート実体化のコスト • 再帰を途中で打ち切るアルゴリズムを考えてみる constexpr auto src_1 = array<int, 10>{{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }}; constexpr auto copied_1 = copy_to_find(src_1, 5); 再帰は 5 まで 要素数が 20 constexpr auto src_2 = array<unsigned, 20>{{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }}; constexpr auto copied_2 = copy_to_find(src_2, 5); 再帰は 5 まで • copied_1 の計算量は copied_2 と変わらないはず – ところが、そうはならない (gcc 4.7) – copied_2 のほうが倍近く遅い
  56. 56. ◆再帰とテンプレート実体化のコスト • 再帰を途中で打ち切るアルゴリズムを考えてみる constexpr auto src_1 = array<int, 10>{{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }}; constexpr auto copied_1 = copy_to_find(src_1, 5); constexpr auto src_2 = array<unsigned, 20>{{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }}; constexpr auto copied_2 = copy_to_find(src_2, 5); • 実際に評価される再帰が何回だったとしても(例え0回 でも)SFINAE で打ち切られるまで全てのテンプレート がインスタンス化されるため – (copied_1 は 10,copied_2 は 20)
  57. 57. ◆再帰とテンプレート実体化のコスト • [回避するには] • インデックスアクセス可能かつ線形探索ならば常に index_tuple イディオムを使う – (再帰のオーバーヘッドも深さ制限もない)
  58. 58. ◆コーディング上のコスト • コンパイル時だとまともにデバックできない – constexpr をマクロにして on/off オフすれば実⾏時デバッグが できる
  59. 59. ◆コーディング上のコスト • 処理フローが増えるたび、別の関数を作って処理を委譲 しなければならない – ループ処理 – ⼀時オブジェクトに名前を付ける
  60. 60. ◆コーディング上のコスト • 例) constexpr uniform_int_distribution の実装 template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size, BaseUnsigned result); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_4(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType result_increment); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType mult, RangeType result_increment); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned, typename Result> constexpr result<T, Engine> generate_uniform_int_true_2_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType mult, Result const& result_increment_base); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result = RangeType(0), RangeType mult = RangeType(1)); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename BaseResult> constexpr result<T, Engine> generate_uniform_int_true_1_1(random_result<Engine> const& rnd, T min_value, BaseResult bmin); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_1(Engine const& eng, T min_value, T max_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T> constexpr result<T, Engine> generate_uniform_int(Engine const& eng, T min_value, T max_value, std::true_type); template<typename Engine, typename T, typename Result> constexpr result<T, Engine> generate_uniform_int_false_1(Result const& rnd); template<typename Engine, typename T>
  61. 61. ◆コーディング上のコスト • 実装関数が増殖する template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size, BaseUnsigned result); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); これは宣⾔だけ書き出して⼀部省略しているので template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> 引数コピペの嵐 実際のコードはもっと酷い constexpr result<T, Engine> generate_uniform_int_true_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); 元々はたった template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_4(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType 2 個の関数だった result, RangeType result_increment); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType 18 個の実装関数 result, RangeType mult, RangeType result_increment); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned, typename Result> constexpr result<T, Engine> generate_uniform_int_true_2_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType mult, Result const& result_increment_base); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result = RangeType(0), RangeType mult = RangeType(1)); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename BaseResult> constexpr result<T, Engine> generate_uniform_int_true_1_1(random_result<Engine> const& rnd, T min_value, BaseResult bmin); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_1(Engine const& eng, T min_value, T max_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T> constexpr result<T, Engine> generate_uniform_int(Engine const& eng, T min_value, T max_value, std::true_type); template<typename Engine, typename T, typename Result> constexpr result<T, Engine> generate_uniform_int_false_1(Result const& rnd); template<typename Engine, typename T>
  62. 62. ◆コーディング上のコスト • 名前付けが酷い template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, generate_uniform_int_true_2_1 とか BaseUnsigned bucket_size, BaseUnsigned result); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); generate_uniform_int_true_2_2 とか template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> generate_uniform_int_true_2_3 ... constexpr result<T, Engine> generate_uniform_int_true_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_4(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType result_increment); そもそも意味論でなく単に処理フローで template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> 分割しているだけなので、 constexpr result<T, Engine> generate_uniform_int_true_2_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType mult, RangeType result_increment); 各関数に意味のある名前を付けるのが難しい template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned, typename Result> constexpr result<T, Engine> generate_uniform_int_true_2_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType mult, Result const& result_increment_base); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result = RangeType(0), RangeType mult = RangeType(1)); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned constexpr ラムダ式があれば brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> 救われる(かもしれない) constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename BaseResult> constexpr result<T, Engine> generate_uniform_int_true_1_1(random_result<Engine> const& rnd, T min_value, BaseResult bmin); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_1(Engine const& eng, T min_value, T max_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T> constexpr result<T, Engine> generate_uniform_int(Engine const& eng, T min_value, T max_value, std::true_type); template<typename Engine, typename T, typename Result> constexpr result<T, Engine> generate_uniform_int_false_1(Result const& rnd); template<typename Engine, typename T>
  63. 63. ◆コーディング上のコスト • [回避するには] • 次期 C++1x に期待する • 諦める
  64. 64. ◆constexpr なライブラリ • 標準ライブラリ • libstdc++ 独⾃拡張 • CEL - ConstExpr Library • Sprout C++ Library
  65. 65. ◆標準ライブラリ • <limits> – numeric_limits • <utility> – pair (デフォルトコンストラクタのみ) • <tuple> – tuple (デフォルトコンストラクタのみ) • <bitset> – bitset • <memory> – unique_ptr (デフォルトコンストラクタのみ) – shared_ptr (デフォルトコンストラクタのみ) – weak_ptr (デフォルトコンストラクタのみ) – enable_shared_from_this (デフォルトコンストラクタのみ)
  66. 66. ◆標準ライブラリ • <ratio> – ratio • <chrono> – 各種演算 – duration_values – duration – time_point • <string> – char_traits • <array> – array (size, max_size, empty のみ) • <iterator> – istream_iterator (デフォルトコンストラクタのみ) – istreambuf_iterator (デフォルトコンストラクタのみ)
  67. 67. ◆標準ライブラリ • <complex> – complex • <random> – 各種⽣成器クラス (min, max のみ) • <regex> – sub_match (デフォルトコンストラクタのみ) • <atomic> – atomic (コンストラクタのみ) • <mutex> – mutex (デフォルトコンストラクタのみ)
  68. 68. ◆標準ライブラリ • 標準ライブラリの⽅針としては、「通常 の使⽤において、ほぼ⾃明にコンパイル 時処理にできる」ものだけを constexpr 指定しているようだ – bitset – ratio – chrono など • numeric_limits が使える⼦になった • 保守的な⽅針
  69. 69. ◆libstdc++ 独⾃拡張 (GCC 4.7 experimental) • <cmath> – 各種関数 • <tuple> – tuple • <utility> – forward, move
  70. 70. ◆libstdc++ 独⾃拡張 • <cmath> が constexpr 化されてるのが ⼤きい – これに依存する前提なら多くの数学計算の constexpr 化が楽になる • なんで <array> が constexpr じゃない のか……
  71. 71. ◆CEL - ConstExpr Library • 作者: RiSK (@sscrisk) さん – https://github.com/sscrisk/CEL---ConstExpr-Library • <algorithm> • <numeric> • <cstdlib> • <cstring> • <iterator> – 各種アルゴリズム (⾮ Mutating な部分)
  72. 72. ◆CEL - ConstExpr Library • <cctype> – ⽂字列判定 • <functional> – 関数オブジェクト • <array> – array • <utility> – pair
  73. 73. ◆CEL - ConstExpr Library • 標準ライブラリの関数の「シグネチャの 変更なしに constexpr 化できるもの」を ほぼ⼀通りカバーしている
  74. 74. ◆Sprout C++ Library • constexpr ⽂字列 • constexpr タプル • constexpr バリアント • constexpr アルゴリズム • constexpr 範囲アルゴリズム • constexpr コンテナ操作 • constexpr 乱数 • constexpr ハッシュ関数 • constexpr UUID • constexpr 構⽂解析 • constexpr レイトレーシング
  75. 75. ◆constexpr ⽂字列 • Sprout.String #include <sprout/string.hpp> #include <iostream> int main() { using namespace sprout; constexpr string<6> hello = to_string("Hello "); constexpr string<6> world = to_string("world!"); constexpr auto str = hello + world; std::cout << str << "¥n"; // "Hello world!" constexpr auto hell = str.substr(0, 4); std::cout << hell << "¥n"; // "Hell" }
  76. 76. ◆constexpr ⽂字列 • Sprout.String #include <sprout/string.hpp> #include <iostream> 型には要素数(最⼤⽂字数)が含まれる int main() { using namespace sprout; constexpr string<6> hello = to_string("Hello "); constexpr string<6> world = to_string("world!"); 要素数が増える場合: constexpr auto str = hello + world; string<N> + string<M> std::cout << str << "¥n"; // "Hello world!" → string<N + M> constexpr auto hell = str.substr(0, 4); std::cout << hell << "¥n"; // "Hell" 要素数が減る場合: } string<N>::substr → string<N>
  77. 77. ◆constexpr ⽂字列 • Sprout.String 実装 namespace sprout { template<class T, size_t N, Traits = char_traits<T> > class basic_string { T elems [N + 1]; ヌル終端を含めた固定⻑バッファ size_t len; }; ⽂字列⻑ (len <= N) } • ⽂字列⻑が変わる操作: – 静的に決定できる場合は型レベルで解決 – そうでなければ len を弄って変更 – あるいはユーザ側に最⼤⻑を指定させる
  78. 78. ◆constexpr アルゴリズム • Sprout.Algorithm #include <sprout/algorithm.hpp> #include <iostream> int main() { using namespace sprout; constexpr auto arr = make_array<int>(5, 1, 9, 4, 8, 2, 7, 3, 10, 6); 配列をソート constexpr auto sorted = sort(arr); for (auto i : sorted) { std::cout << i << ' '; } std::cout << "¥n"; // 1 2 3 4 5 6 7 8 9 10 配列を反転 constexpr auto reversed = reverse(sorted); for (auto i : reversed) { std::cout << i << ' '; } std::cout << "¥n"; // 10 9 8 7 6 5 4 3 2 1 }
  79. 79. ◆constexpr アルゴリズム • Sprout.Algorithm – 主に Mutating sequence operations をカバーする • CEL と合わせれば STL のアルゴリズムはほぼ全てカ バーできる • RandomAccessIterator に対しては index_tuple イ ディオムで効率的に処理する – ユーザ側で sprout::next/prev をオーバーロードすることで InputIterator なども渡せる • それ以外の IteratorCategory は Variadic Function で 実装されている
  80. 80. ◆constexpr 乱数 • Sprout.Random #include <sprout/random.hpp> #include <sprout/random/unique_seed.hpp> #include <sprout/array.hpp> #include <sprout/algorithm.hpp> #include <iostream> int main() { using namespace sprout; constexpr auto arr = make_array<int>(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); constexpr std::size_t seed = SPROUT_UNIQUE_SEED; constexpr auto engine = minstd_rand0(seed); constexpr auto distribution = uniform_smallint<int>(1, 6); constexpr auto shuffled = shuffle(arr, random::combine(engine, distribution)); for (auto i : shuffled) { std::cout << i << ' '; } std::cout << "¥n"; }
  81. 81. ◆constexpr 乱数 • Sprout.Random #include <sprout/random.hpp> #include <sprout/random/unique_seed.hpp> #include <sprout/array.hpp> #include <sprout/algorithm.hpp> ⽇時とファイル名と⾏の #include <iostream> ⽂字列からハッシュ値を ⽣成するマクロ int main() { using namespace sprout; constexpr auto arr = make_array<int>(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); constexpr std::size_t seed = SPROUT_UNIQUE_SEED; 線形合同法エンジン constexpr auto engine = minstd_rand0(seed); constexpr auto distribution = uniform_smallint<int>(1, 6); 整数⼀様分布 constexpr auto shuffled = shuffle(arr, random::combine(engine, distribution)); for (auto i : shuffled) { std::cout << i << ' '; } std::cout << "¥n"; ランダムシャッフル }
  82. 82. ◆constexpr 乱数 • Sprout.Random – <random> と同じく様々な乱数⽣成器と分布を提供する • 関数型⾔語にならって、乱数⽣成器は「⽣成した値」 「次の状態の⽣成器」のペアを返す仕様 • コンパイル時に取得できる値でシードに使えるものが __DATA__ __FILE__ __LINE__ くらいしかないので それを使う
  83. 83. ◆constexpr 構⽂解析 • Sprout.Weed #include <sprout/weed.hpp> #include <sprout/string.hpp> #include <sprout/uuid.hpp> Int main() { using namespace sprout; using namespace sprout::weed; constexpr auto s = to_string("{550E8400-E29B-41D4-A716-446655440000}"); constexpr auto unbracket_uuid_p = repeat[lim<16>(hex8f)] | repeat[lim<4>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<6>(hex8f)]; constexpr auto uuid_p = (unbracket_uuid_p | '{' >> unbracket_uuid_p >> '}') >> eoi; constexpr auto parsed = parse(s.begin(), s.end(), uuid_p); }
  84. 84. ◆constexpr 構⽂解析 • Sprout.Weed #include <sprout/weed.hpp> #include <sprout/string.hpp> #include <sprout/uuid.hpp> hex8f : 8bit16進数にマッチ Int main() { using namespace sprout; using namespace sprout::weed; constexpr auto s = to_string("{550E8400-E29B-41D4-A716-446655440000}"); constexpr auto unbracket_uuid_p = repeat[lim<16>(hex8f)] | repeat[lim<4>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<6>(hex8f)]; constexpr auto uuid_p = (unbracket_uuid_p | '{' >> unbracket_uuid_p >> '}') >> eoi; constexpr auto parsed = parse(s.begin(), s.end(), uuid_p); }
  85. 85. ◆constexpr 構⽂解析 • Sprout.Weed #include <sprout/weed.hpp> #include <sprout/string.hpp> repeat : lim<N>回の繰り返し #include <sprout/uuid.hpp> array<Attr, N * M> Int main() { using namespace sprout; using namespace sprout::weed; constexpr auto s = to_string("{550E8400-E29B-41D4-A716-446655440000}"); constexpr auto unbracket_uuid_p = repeat[lim<16>(hex8f)] | repeat[lim<4>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<6>(hex8f)]; constexpr auto uuid_p = (unbracket_uuid_p | '{' >> unbracket_uuid_p >> '}') >> eoi; constexpr auto parsed = parse(s.begin(), s.end(), uuid_p); }
  86. 86. ◆constexpr 構⽂解析 • Sprout.Weed #include <sprout/weed.hpp> #include <sprout/string.hpp> >> : パーサの連結 #include <sprout/uuid.hpp> array<Attr, N + M> Int main() { using namespace sprout; using namespace sprout::weed; constexpr auto s = to_string("{550E8400-E29B-41D4-A716-446655440000}"); constexpr auto unbracket_uuid_p = repeat[lim<16>(hex8f)] | repeat[lim<4>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<6>(hex8f)]; constexpr auto uuid_p = (unbracket_uuid_p | '{' >> unbracket_uuid_p >> '}') >> eoi; constexpr auto parsed = parse(s.begin(), s.end(), uuid_p); }
  87. 87. ◆constexpr 構⽂解析 • Sprout.Weed #include <sprout/weed.hpp> #include <sprout/string.hpp> | : パーサのOR #include <sprout/uuid.hpp> variant<Attr1, Attr2> Int main() { using namespace sprout; using namespace sprout::weed; constexpr auto s = to_string("{550E8400-E29B-41D4-A716-446655440000}"); constexpr auto unbracket_uuid_p = repeat[lim<16>(hex8f)] | repeat[lim<4>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<2>(hex8f)] >> '-' >> repeat[lim<6>(hex8f)]; constexpr auto uuid_p = (unbracket_uuid_p | '{' >> unbracket_uuid_p >> '}') >> eoi; constexpr auto parsed = parse(s.begin(), s.end(), uuid_p); }
  88. 88. ◆constexpr 構⽂解析 • Sprout.Weed – Boost.Spirit.Qi のような Expression Template ベースの EDSL 構⽂解析ライブラリ • Expression Template 技法では、処理が細かい単位に 分割されるので、constexpr 化しやすい • constexpr の導⼊によって、コンパイル時⽂字列処理が 相当⼿軽に出来るようになった • おそらく今後 constexpr 正規表現などのライブラリも 出てくると思われる
  89. 89. ◆constexpr レイトレーシング • Sprout.Darkroom
  90. 90. ◆constexpr レイトレーシング • Sprout.Darkroom #include <sprout/darkroom.hpp> 512×512 pixel #include <iostream> で作成 static constexpr std::size_t total_width = 512; static constexpr std::size_t total_height = 512; static constexpr std::size_t tile_width = 16; static constexpr std::size_t tile_height = 16; static constexpr std::size_t offset_x = 0; static constexpr std::size_t offset_y = 0; using namespace sprout; using namespace sprout::darkroom;
  91. 91. ◆constexpr レイトレーシング • Sprout.Darkroom constexpr auto object = make_tuple( objects::make_sphere( coords::vector3d(-1.0, -0.5, 5.0), 1.0, オブジェクトの定義 materials::make_material_image( (2つの球) colors::rgb_f(1.0, 0.75, 0.75), 0.2) ), objects::make_sphere( coords::vector3d(0.5, 0.5, 3.5), 1.0, materials::make_material_image( colors::rgb_f(0.75, 0.75, 1.0), 0.2) ) );
  92. 92. ◆constexpr レイトレーシング • Sprout.Darkroom 光源の定義 (点光源) constexpr auto light = lights::make_point_light( coords::vector3d(1.0, 0.5, 1.0), colors::rgb_f(3.0, 3.0, 3.0)); constexpr auto camera = cameras::make_simple_camera(1.0); constexpr auto renderer = renderers::whitted_style(); constexpr auto raytracer = tracers::raytracer<>(); カメラ レンダラ レイトレーサー
  93. 93. ◆constexpr レイトレーシング • Sprout.Darkroom ピクセル⽣成 typedef pixels::color_pixels<tile_width, tile_height>::type image_type; constexpr auto image = pixels::generate<image_type>( raytracer, renderer, camera, object, light, offset_x, offset_y, total_width, total_height);
  94. 94. ◆constexpr レイトレーシング • Sprout.Darkroom std::cout << "P3" << std::endl << image[0].size() << ' ' << image.size() << std::endl << 255 << std::endl ; for (auto const& line : image) { 標準出⼒へ for (auto const& pixel : line) { ppm 形式で出⼒ std::cout << unsigned(colors::r(pixel)) << ' ' << unsigned(colors::g(pixel)) << ' ' << unsigned(colors::b(pixel)) << std::endl; } } }
  95. 95. ◆constexpr レイトレーシング • Sprout.Darkroom – metatrace という TMP ライブラリを元にした constexpr レイ トレーサーライブラリ • レイトレーシングの基本的なアルゴリズムはとてもシン プル • 視点から各ピクセルを通る光線を⾶ばして、ベクトルが オブジェクトと衝突する部分の拡散光と反射光の成分を 得るだけ
  96. 96. ◆次期 C++1x に期待すること • いくつかのポインタ演算を定数式扱いに p1 < p2; ポインタの⼤⼩⽐較はできない p1 - p2; ポインタ同⼠の減算はできない static_cast<int const*>( static_cast<void const*>(p) ) void* から他の型への読み替えはできない • union の任意のメンバでの初期化を定数式扱いに union U { X x; Y y; constexpr U() : y() { } }; 先頭メンバ以外での初期化はできない
  97. 97. ◆次期 C++1x に期待すること • ラムダ式を定数式扱いに template<class F> constexpr auto call(F f) -> decltype(f()) { return f(); } call( []{ return 0; } ); constexpr 関数にラムダ式を渡せない • new/delete を定数式に出来るように • 標準ライブラリを更に constexpr 化
  98. 98. ◆constexpr 化できそうなもの • 正規表現 – Regex / Expressive • 汎⽤ Expression Template – Proto • シリアライズ – Serialization • 画像処理 – GIL • 数学関数 – Math.SpecialFunction • etc...
  99. 99. ◆まとめ • constexpr で表現可能な応⽤範囲はとて も広い • コーディング上の制約と落とし⽳は多い ので気をつける • 数値計算が得意分野 • constexpr で遊ぼう!
  100. 100. ご静聴ありがとう ございました

×