0
中3女子でもわかる!                  constexprBoost.勉強会 #7bolero_MURAKAMI2011/12/3
◆⾃⼰紹介• 名前 : 村上 原野    (むらかみ げんや)      @bolero_MURAKAMI, id:boleros• 棲息地: ⼤都会岡⼭• 仕事 : 猪⾵来美術館陶芸指導員 ・普段はやきものの修⾏をしたり、  縄⽂⼟器をつくっ...
◆⾃⼰紹介• 好きな C++11 の機能:   constexpr• 嫌いな C++11 のキーワード:   constexpr• 次期 C++ で強化されてほしい機能:   constexpr• 次期 C++ で消えてほしいキーワード:   ...
◆⾃⼰紹介• 公開しているライブラリ: Sprout C++ Library (constexpr ライブラリ) github.com/bolero-MURAKAMI/Sprout
◆アジェンダ•   はじめに•   constexpr   とは?•   constexpr   実装技法•   constexpr   の3⼤コスト•   constexpr   ライブラリを使ってみる•   まとめ
◆constexpr とは?• N32907.1.5 The constexpr specifier [dcl.constexpr]1 The constexpr specifier shall be applied only to the d...
◆constexpr とは?• N3290 7.1.5 constexpr 指定子 [dcl.constexpr] constexpr 指定子は、変数の定義、関数または関数テンプレートの宣言、 またはリテラル型(3.9)の静的データメンバの宣言...
◆constexpr とは?• constexpr 宣⾔された変数は、コンパイル時定数になるconstexpr int zero = 0;                           // constexpr 変数using zero_...
◆constexpr ⼊⾨• プログラミングの魔導書vol.2 の江添さん  の記事を読んでください• 読んでなかったら買ってください
◆C++ プログラミングのレイヤー   C++03         [処理されるもの]     [プログラマのすること] プリプロセス時の世界                       プリプロセッサ    (魔界)           ソー...
◆C++ プログラミングのレイヤー   C++03         [処理されるもの]     [プログラマのすること] プリプロセス時の世界                       プリプロセッサ    (魔界)           ソー...
◆C++ プログラミングのレイヤー   C++11         [処理されるもの]      [プログラマのすること] プリプロセス時の世界                        プリプロセッサ    (魔界)           ...
◆定数も⾮定数も constexpr で• TMP で定数値を計算するtypedef typename mpl::max<A, B>::type value_t;constexpr auto compiletime_value = value_...
◆定数も⾮定数も constexpr で• TMP で定数値を計算する typedef typename mpl::max<A, B>::type value_t; constexpr auto compiletime_value = valu...
◆コンパイル時⽂字列• TMP でやってみるtypedef mpl::string<’Hell’, ’o, wo’, ’rld! ’> hello_t;  –   書きづらい(醜い)  –   遅い  –   制限が多い  –   型の領域だけ...
◆コンパイル時⽂字列• TMP でやってみる typedef mpl::string<’Hell’, ’o, wo’, ’rld! ’> hello_t;   –   書きづらい(醜い)   –   遅い   –   制限が多い   –   型...
◆コンパイル時⽂字列• こんなことも出来るよ!    – 例) コンパイル時⽂字列解析 (Boost.Spirit.Qi ⾵){    using namespace sprout;    using namespace sprout::wee...
◆コンパイル時⽂字列• こんなことも出来るよ!    – 例) コンパイル時⽂字列解析 (Boost.Spirit.Qi ⾵){    using namespace sprout;                               ...
◆constexpr で扱えるデータ                           [リテラル型]                [スカラ型]                        [リテラル型の配列]              ...
◆リテラル型クラスの条件• コンパイラの要求 – trivial コピーコンストラクタを持つ – ⾮ trivial ムーブコンストラクタを持たない – trivial デストラクタを持つ – trivial デフォルトコンストラクタか、コピー...
◆リテラル型クラスの条件• プログラマの「べからず」 – 仮想関数や仮想基底クラスを書かない – ユーザ定義コピーコンストラクタを書かない   • (delete もしない) – ユーザ定義ムーブコンストラクタを書かない   • (delete...
◆リテラル型クラスの条件• リテラル型になれない例struct NonLiteralType                  仮想基底クラスは NG!    : virtual BaseType    : NonLiteralBaseType...
◆リテラル型クラスの条件• リテラル型になれない例struct NonLiteralType {                             ユーザ定義デストラクタは NG!   ~LiteralType() { }};      ...
◆リテラル型クラスの条件• リテラル型になれない例struct NonLiteralType {private:                      リテラル型以外のデータメンバは NG!   NonLiteralDataType dat...
◆関数を constexpr 化する•   条件分岐•   ループ•   ローカル変数•   エラー通知•   シグネチャの constexpr 化•   配列操作 (Variadic function)•   配列操作 (index_tupl...
◆条件分岐• ⾮ constexpr 関数 template<class T> T max(T const& a, T const& b) {   if (a < b) return b;   else return a; }         ...
◆ループ• ⾮ constexpr 関数 template<class Iter, class T> Iter find(Iter first, Iter last, T const& val) {    while (first != las...
◆ループ• ⾮ constexpr 関数 template<class Iter, class T> Iter find(Iter first, Iter last, T const& val) {    while (first != las...
◆ローカル変数• ⾮ constexpr 関数 double heron(double a, double b, double c) {   double s = (a+b+c)/2;   return std::sqrt(s*(s-a)*(s...
◆エラー通知• ⾮ constexpr 関数 template<class T> T const* next(T const* p) {   if (p) return p + 1;   else assert(0); // error }  ...
◆エラー通知• ⾮ constexpr 関数 template<class T> T const* next(T const* p) {   if (p) return p + 1;   else assert(0); // error }  ...
◆シグネチャの constexpr 化• ⾮ constexpr 関数 template<class T> void inc(T& x) {   ++x; }                                  返値 void →...
◆シグネチャの constexpr 化• ⾮ constexpr 関数 template<class Iter, class Expr, class Attr> bool parse(Iter& first, Iter last, Expr c...
◆配列操作 (Variadic function)• reverse アルゴリズムを実装してみる #include <sprout/array.hpp>                                      constexp...
◆配列操作 (Variadic function)• reverse アルゴリズムを実装してみる template<class T, size_t N, class... Args> constexpr array<T, N> reverse_...
◆配列操作 (Variadic function)• reverse アルゴリズムを実装してみる template<class T, size_t N, class... Args> constexpr array<T, N> reverse_...
◆配列操作 (Variadic function)• reverse アルゴリズムを実装してみる template<class T, size_t N, class... Args> constexpr array<T, N> reverse_...
◆配列操作 (Variadic function)• reverse アルゴリズムを実装してみる template<class T, size_t N, class... Args> constexpr typename enable_if< ...
◆配列操作 (Variadic function)• Variadic function の再帰はテンプレート実体化の無限  再帰になりやすいので注意する• そうなるケースでは再帰条件を SFINAE にする• (static if ほしいです)
◆配列操作 (index_tuple idiom)• また reverse アルゴリズムを実装してみる #include <sprout/array.hpp>                                      const...
◆配列操作 (index_tuple idiom)• また reverse アルゴリズムを実装してみる #include <sprout/index_tuple.hpp>          for index_tuple, index_rang...
◆配列操作 (index_tuple idiom)• また reverse アルゴリズムを実装してみる #include <sprout/index_tuple.hpp> template<class T, size_t N, ptrdiff_...
◆配列操作 (index_tuple idiom)• index_tuple イディオムは、インデックスアクセス可能  なデータ構造⼀般に適⽤できる –   配列 –   タプル –   ランダムアクセスイテレータ –   型リスト• cons...
◆クラスを constexpr 化する• コンストラクタで処理を委譲• 状態を書き換えるメンバ関数の  constexpr 化
◆コンストラクタで処理を委譲• Delegating constructor #include <sscrisk/cel/algorithm.hpp>     constexpr minmax_element using namespace s...
◆コンストラクタで処理を委譲• Delegating constructor template<class T> struct minmax_t {                                まず minmax を求めてから...
◆コンストラクタで処理を委譲• C++03 Delegating constructor workaround template<class T> struct minmax_t_impl { protected:    template<cl...
◆状態を書き換えるメンバ関数• ⾮ constexpr クラス struct sha1 {    void process_byte(unsigned char byte);    template<class Iter>           ...
◆状態を書き換えるメンバ関数• ⾮ constexpr クラス struct random_generator {    unsigned int operator()();                  状態を書き換え、 };      ...
◆constexpr の3⼤コスト• オブジェクトコピーのコスト• 再帰とテンプレートインスタンス化のコ  スト• コーディング上のコスト (⼈間のコスト)
◆オブジェクトコピーのコスト• 配列の2つの要素の swap を考えてみる  – ⾮ constexpr の場合 (元の配列を書換え)⾼々1回のコピーと  2回の代⼊(またはムーヴ)  – constexpr の場合 (別の配列をつくる) 常に...
◆オブジェクトコピーのコスト• 状態を書き換えるメンバ関数の呼出しを考えてみる constexpr array<unsigned char, N> src = { /*...*/ }; sha1.process_byte(src[0])   ....
◆オブジェクトコピーのコスト• [回避するには]• 状態を変更する呼出しは可能な限り少なくする  – (1つの関数で済ませる)• Variadic function や index_tuple イディオムを活⽤し  てオブジェクトコピーが⾏われ...
◆再帰とテンプレート実体化のコスト• 再帰を途中で打ち切るアルゴリズムを考えてみるtemplate<class T, size_t N, class... Args>constexpr typename enable_if<          ...
◆再帰とテンプレート実体化のコスト• 再帰を途中で打ち切るアルゴリズムを考えてみる constexpr auto src_1 = array<int, 10>{{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }}; conste...
◆再帰とテンプレート実体化のコスト• 再帰を途中で打ち切るアルゴリズムを考えてみるconstexpr auto src_1 = array<int, 10>{{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }};constexp...
◆再帰とテンプレート実体化のコスト• [回避するには]• インデックスアクセス可能かつ線形探索ならば常に  index_tuple イディオムを使う – (再帰のオーバーヘッドも深さ制限もない)
◆コーディング上のコスト• コンパイル時だとまともにデバックできない – constexpr をマクロにして on/off オフすれば実⾏時デバッグが   できる
◆コーディング上のコスト• 処理フローが増えるたび、別の関数を作って処理を委譲  しなければならない – ループ処理 – ⼀時オブジェクトに名前を付ける
◆コーディング上のコスト• 例) constexpr uniform_int_distribution の実装 template<typename Engine, typename T, typename RangeType, typename...
◆コーディング上のコスト• 実装関数が増殖するtemplate<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigne...
◆コーディング上のコスト• 名前付けが酷いtemplate<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>...
◆コーディング上のコスト• [回避するには]• 次期 C++1x に期待する• 諦める
◆constexpr なライブラリ•   標準ライブラリ•   libstdc++ 独⾃拡張•   CEL - ConstExpr Library•   Sprout C++ Library
◆標準ライブラリ• <limits>   – numeric_limits• <utility>   – pair (デフォルトコンストラクタのみ)• <tuple>   – tuple (デフォルトコンストラクタのみ)• <bitset>  ...
◆標準ライブラリ• <ratio>   – ratio• <chrono>   –   各種演算   –   duration_values   –   duration   –   time_point• <string>   – char_...
◆標準ライブラリ• <complex>   – complex• <random>   – 各種⽣成器クラス (min, max のみ)• <regex>   – sub_match (デフォルトコンストラクタのみ)• <atomic>   –...
◆標準ライブラリ• 標準ライブラリの⽅針としては、「通常  の使⽤において、ほぼ⾃明にコンパイル  時処理にできる」ものだけを constexpr  指定しているようだ – bitset – ratio – chrono など• numeric...
◆libstdc++ 独⾃拡張     (GCC 4.7 experimental)• <cmath>  – 各種関数• <tuple>  – tuple• <utility>  – forward, move
◆libstdc++ 独⾃拡張• <cmath> が constexpr 化されてるのが  ⼤きい – これに依存する前提なら多くの数学計算の   constexpr 化が楽になる• なんで <array> が constexpr じゃない  ...
◆CEL - ConstExpr Library• 作者: RiSK (@sscrisk) さん    – https://github.com/sscrisk/CEL---ConstExpr-Library•   <algorithm>•  ...
◆CEL - ConstExpr Library• <cctype>   – ⽂字列判定• <functional>   – 関数オブジェクト• <array>   – array• <utility>   – pair
◆CEL - ConstExpr Library• 標準ライブラリの関数の「シグネチャの  変更なしに constexpr 化できるもの」を  ほぼ⼀通りカバーしている
◆Sprout C++ Library•   constexpr   ⽂字列•   constexpr   タプル•   constexpr   バリアント•   constexpr   アルゴリズム•   constexpr   範囲アルゴリ...
◆constexpr ⽂字列• Sprout.String #include <sprout/string.hpp> #include <iostream> int main() {    using namespace sprout;    ...
◆constexpr ⽂字列• Sprout.String #include <sprout/string.hpp> #include <iostream>                                         型には...
◆constexpr ⽂字列• Sprout.String 実装 namespace sprout {   template<class T, size_t N, Traits = char_traits<T> >   class basic_...
◆constexpr アルゴリズム• Sprout.Algorithm #include <sprout/algorithm.hpp> #include <iostream> int main() {    using namespace sp...
◆constexpr アルゴリズム• Sprout.Algorithm  – 主に Mutating sequence operations をカバーする• CEL と合わせれば STL のアルゴリズムはほぼ全てカ  バーできる• Random...
◆constexpr 乱数• Sprout.Random #include <sprout/random.hpp> #include <sprout/random/unique_seed.hpp> #include <sprout/array....
◆constexpr 乱数• Sprout.Random #include <sprout/random.hpp> #include <sprout/random/unique_seed.hpp> #include <sprout/array....
◆constexpr 乱数• Sprout.Random  – <random> と同じく様々な乱数⽣成器と分布を提供する• 関数型⾔語にならって、乱数⽣成器は「⽣成した値」  「次の状態の⽣成器」のペアを返す仕様• コンパイル時に取得できる値...
◆constexpr 構⽂解析• Sprout.Weed #include <sprout/weed.hpp> #include <sprout/string.hpp> #include <sprout/uuid.hpp> Int main()...
◆constexpr 構⽂解析• Sprout.Weed #include <sprout/weed.hpp> #include <sprout/string.hpp> #include <sprout/uuid.hpp>           ...
◆constexpr 構⽂解析• Sprout.Weed #include <sprout/weed.hpp> #include <sprout/string.hpp>              repeat : lim<N>回の繰り返し #i...
◆constexpr 構⽂解析• Sprout.Weed #include <sprout/weed.hpp> #include <sprout/string.hpp>                    >> : パーサの連結 #inclu...
◆constexpr 構⽂解析• Sprout.Weed #include <sprout/weed.hpp> #include <sprout/string.hpp>                      | : パーサのOR #incl...
◆constexpr 構⽂解析• Sprout.Weed  – Boost.Spirit.Qi のような Expression Template ベースの    EDSL 構⽂解析ライブラリ• Expression Template 技法では、...
◆constexpr レイトレーシング• Sprout.Darkroom
◆constexpr レイトレーシング• Sprout.Darkroom #include <sprout/darkroom.hpp>                     512×512 pixel #include <iostream> ...
◆constexpr レイトレーシング• Sprout.Darkroom  constexpr auto object = make_tuple(    objects::make_sphere(       coords::vector3d(...
◆constexpr レイトレーシング• Sprout.Darkroom                                                     光源の定義                            ...
◆constexpr レイトレーシング• Sprout.Darkroom                                                     ピクセル⽣成  typedef pixels::color_pix...
◆constexpr レイトレーシング• Sprout.Darkroom     std::cout        << "P3" << std::endl        << image[0].size() <<   << image.siz...
◆constexpr レイトレーシング• Sprout.Darkroom  – metatrace という TMP ライブラリを元にした constexpr レイ    トレーサーライブラリ• レイトレーシングの基本的なアルゴリズムはとてもシン...
◆次期 C++1x に期待すること• いくつかのポインタ演算を定数式扱いにp1 < p2;                              ポインタの⼤⼩⽐較はできないp1 - p2;                         ...
◆次期 C++1x に期待すること• ラムダ式を定数式扱いにtemplate<class F>constexpr auto call(F f) -> decltype(f()) { return f(); }call( []{ return 0...
◆constexpr 化できそうなもの• 正規表現   – Regex / Expressive• 汎⽤ Expression Template   – Proto• シリアライズ   – Serialization• 画像処理   – GIL...
◆まとめ• constexpr で表現可能な応⽤範囲はとて  も広い• コーディング上の制約と落とし⽳は多い  ので気をつける• 数値計算が得意分野• constexpr で遊ぼう!
ご静聴ありがとう ございました
Upcoming SlideShare
Loading in...5
×

中3女子でもわかる constexpr

24,216

Published on

Boost.勉強会 #7 中3女子でもわかる constexpr

Published in: Technology
1 Comment
40 Likes
Statistics
Notes
  • スライド内の記述と C++11 の現行規格とに齟齬があったので、訂正記事を付記します。 http://d.hatena.ne.jp/boleros/20130718
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
24,216
On Slideshare
0
From Embeds
0
Number of Embeds
15
Actions
Shares
0
Downloads
93
Comments
1
Likes
40
Embeds 0
No embeds

No notes for slide

Transcript of "中3女子でもわかる constexpr"

  1. 1. 中3女子でもわかる! constexprBoost.勉強会 #7bolero_MURAKAMI2011/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 とは?• N32907.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 astatic data member of a literal type (3.9). If any declaration of a function orfunction template has constexpr specifier, then all its declarations shallcontain the constexpr specifier. [ Note: An explicit specialization can differfrom the template declaration with respect to the constexpr specifier. -endnote ] [ 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, BaseUnsignedbrange, 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, BaseUnsignedbrange, 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, RangeTyperesult, 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, RangeTypelimit, 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, BaseUnsignedbrange, 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, RangeTypelimit, 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, BaseUnsignedbrange, 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, BaseUnsignedbrange, 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, RangeTyperesult, 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, RangeTyperesult, 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, RangeTyperesult, 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, RangeTypelimit, 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, RangeTypelimit, 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. ご静聴ありがとう ございました
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×