Movatterモバイル変換


[0]ホーム

URL:


Genya Murakami, profile picture
Uploaded byGenya Murakami
PDF, PPTX50,193 views

中3女子でもわかる constexpr

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

Embed presentation

Download as PDF, PPTX
中3女子でもわかる!                  constexprBoost.勉強会 #7bolero_MURAKAMI2011/12/3
◆⾃⼰紹介• 名前 : 村上 原野    (むらかみ げんや)      @bolero_MURAKAMI, id:boleros• 棲息地: ⼤都会岡⼭• 仕事 : 猪⾵来美術館陶芸指導員 ・普段はやきものの修⾏をしたり、  縄⽂⼟器をつくったりしています ・趣味は constexpr です
◆⾃⼰紹介• 好きな C++11 の機能:   constexpr• 嫌いな C++11 のキーワード:   constexpr• 次期 C++ で強化されてほしい機能:   constexpr• 次期 C++ で消えてほしいキーワード:   constexpr
◆⾃⼰紹介• 公開しているライブラリ: 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 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 ]
◆constexpr とは?• N3290 7.1.5 constexpr 指定子 [dcl.constexpr] constexpr 指定子は、変数の定義、関数または関数テンプレートの宣言、 またはリテラル型(3.9)の静的データメンバの宣言に適用されるものとする。 (中略) [注:関数のパラメータは constexpr 宣言することはできない。]• C++11 で導⼊されたキーワードである• decl-specifier に分類される   – (型修飾⼦ではない)
◆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{ }; // クラスインスタンスを定数式に
◆constexpr ⼊⾨• プログラミングの魔導書vol.2 の江添さん  の記事を読んでください• 読んでなかったら買ってください
◆C++ プログラミングのレイヤー   C++03         [処理されるもの]     [プログラマのすること] プリプロセス時の世界                       プリプロセッサ    (魔界)           ソースコード                                 メタプログラミング                                 テンプレート                     型  コンパイル時の世界                     メタプログラミング(ライブラリアンが多数棲息)                      定数式   実⾏時の世界          実⾏時オブジェクト    (⼈間界)                                 通常の                                プログラミング
◆C++ プログラミングのレイヤー   C++03         [処理されるもの]     [プログラマのすること] プリプロセス時の世界                       プリプロセッサ    (魔界)           ソースコード                                 メタプログラミング                                 テンプレート                     型  コンパイル時の世界                     メタプログラミング(ライブラリアンが多数棲息)                      定数式     どっちも「値」を   求めるのは同じでも……                                   わたしたち                                 離ればなれね……   実⾏時の世界          実⾏時オブジェクト    (⼈間界)                                 通常の                                プログラミング
◆C++ プログラミングのレイヤー   C++11         [処理されるもの]      [プログラマのすること] プリプロセス時の世界                        プリプロセッサ    (魔界)            ソースコード                                  メタプログラミング                                  テンプレート      それ constexpr                      型                        で出来るよ!  コンパイル時の世界                      メタプログラミング(ライブラリアンが多数棲息)                       定数式            抱いて!!                             constexpr                                 キャーカッコイー!!   実⾏時の世界           実⾏時オブジェクト    (⼈間界)                                  通常の                                 プログラミング
◆定数も⾮定数も 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 で• 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);
◆コンパイル時⽂字列• TMP でやってみるtypedef mpl::string<’Hell’, ’o, wo’, ’rld! ’> hello_t;  –   書きづらい(醜い)  –   遅い  –   制限が多い  –   型の領域だけでは無理がある
◆コンパイル時⽂字列• 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!”);                                                          ⽂字列リテラルも                                                           そのまま使える
◆コンパイル時⽂字列• こんなことも出来るよ!    – 例) コンパイル時⽂字列解析 (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";}
◆コンパイル時⽂字列• こんなことも出来るよ!    – 例) コンパイル時⽂字列解析 (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";}             最後以外全部コンパイル時に                 処理される
◆constexpr で扱えるデータ                           [リテラル型]                [スカラ型]                        [リテラル型の配列]                 [算術型]                          LiteralType [N]     [整数型] int, unsigned int, char, ...                                              [リテラル型への参照]     [浮動⼩数点型] float, double, ...               LiteralType const&               [ポインタ型]  [ポインタ] int const*, int (*)(void), ...                                              特定の条件を満たす  [メンバポインタ] int T::*, int (T::*)(void), ...                                               ユーザ定義クラス               [列挙型] enum
◆リテラル型クラスの条件• コンパイラの要求 – trivial コピーコンストラクタを持つ – ⾮ trivial ムーブコンストラクタを持たない – trivial デストラクタを持つ – trivial デフォルトコンストラクタか、コピーでもムーブでもな   い constexpr コンストラクタを持つ – ⾮ static データメンバと基本クラスは、全てリテラル型である
◆リテラル型クラスの条件• プログラマの「べからず」 – 仮想関数や仮想基底クラスを書かない – ユーザ定義コピーコンストラクタを書かない   • (delete もしない) – ユーザ定義ムーブコンストラクタを書かない   • (delete するのはよい) – ユーザ定義デストラクタを書かない – ユーザ定義デフォルトコンストラクタを書くなら原則   constexpr にする   • (デフォルトコンストラクタが trivial でも constexpr でもない場     合、別の constexpr コンストラクタを書く) – リテラル型以外の⾮ static データメンバや基底クラスを使わな   い
◆リテラル型クラスの条件• リテラル型になれない例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!
◆リテラル型クラスの条件• リテラル型になれない例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
◆リテラル型クラスの条件• リテラル型になれない例struct NonLiteralType {private:                      リテラル型以外のデータメンバは NG!   NonLiteralDataType data_member_;};
◆関数を constexpr 化する•   条件分岐•   ループ•   ローカル変数•   エラー通知•   シグネチャの constexpr 化•   配列操作 (Variadic function)•   配列操作 (index_tuple イディオム)
◆条件分岐• ⾮ 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; }
◆ループ• ⾮ 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;                         ++演算⼦は使えない }                                     (副作⽤があるから)
◆ループ• ⾮ 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); }                                                    式を整理して                                                    このようにも書ける
◆ローカル変数• ⾮ 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++ の⾮標準拡張
◆エラー通知• ⾮ 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"); }                                   コンパイル時にはコンパイルエラー                                       実⾏時には例外が投げられる
◆エラー通知• ⾮ 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 は⾮定数式
◆シグネチャの constexpr 化• ⾮ constexpr 関数 template<class T> void inc(T& x) {   ++x; }                                  返値 void → 値を返す• constexpr 関数                   引数書換え → 副作⽤無しに template<class T> constexpr T inc(T const& x) {   return x + 1; }
◆シグネチャの 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);
◆配列操作 (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);                         適⽤結果の配列を返す
◆配列操作 (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); }
◆配列操作 (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)                    テンプレート実体化の深さ限界を超えてしまった
◆配列操作 (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); }                                結果、テンプレートの実体化が                                      無限再帰になる
◆配列操作 (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); }
◆配列操作 (Variadic function)• Variadic function の再帰はテンプレート実体化の無限  再帰になりやすいので注意する• そうなるケースでは再帰条件を SFINAE にする• (static if ほしいです)
◆配列操作 (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);                         適⽤結果の配列を返す
◆配列操作 (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()); }
◆配列操作 (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> を返す
◆配列操作 (index_tuple idiom)• index_tuple イディオムは、インデックスアクセス可能  なデータ構造⼀般に適⽤できる –   配列 –   タプル –   ランダムアクセスイテレータ –   型リスト• constexpr に限らず、通常の関数や TMP にも同じよう  に応⽤できる• ただしインデックスアクセス不可なデータ構造には適⽤  できない – ⾮ランダムアクセスなイテレータなど
◆クラスを constexpr 化する• コンストラクタで処理を委譲• 状態を書き換えるメンバ関数の  constexpr 化
◆コンストラクタで処理を委譲• 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_;                                   初期化⼦リストにしか };                                                    処理を書けない
◆コンストラクタで処理を委譲• 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_; };
◆コンストラクタで処理を委譲• 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
◆状態を書き換えるメンバ関数• ⾮ 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; };
◆状態を書き換えるメンバ関数• ⾮ constexpr クラス struct random_generator {    unsigned int operator()();                  状態を書き換え、 };                                            かつ処理結果を返す                                                   ↓                                              処理結果と「次の状態」の                                                 タプルを返す• constexprクラス struct random_generator {    constexpr pair<unsigned int, random_generator> operator()() const; };
◆constexpr の3⼤コスト• オブジェクトコピーのコスト• 再帰とテンプレートインスタンス化のコ  スト• コーディング上のコスト (⼈間のコスト)
◆オブジェクトコピーのコスト• 配列の2つの要素の swap を考えてみる  – ⾮ constexpr の場合 (元の配列を書換え)⾼々1回のコピーと  2回の代⼊(またはムーヴ)  – constexpr の場合 (別の配列をつくる) 常に全要素の   コピー• 単なる要素の交換に線形時間 O(n) を要する
◆オブジェクトコピーのコスト• 状態を書き換えるメンバ関数の呼出しを考えてみる 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 にすると、  (オブジェクトコピー×呼出し回数)の計算量が余計に  かかる
◆オブジェクトコピーのコスト• [回避するには]• 状態を変更する呼出しは可能な限り少なくする  – (1つの関数で済ませる)• Variadic function や index_tuple イディオムを活⽤し  てオブジェクトコピーが⾏われない処理を書く
◆再帰とテンプレート実体化のコスト• 再帰を途中で打ち切るアルゴリズムを考えてみる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)]);}
◆再帰とテンプレート実体化のコスト• 再帰を途中で打ち切るアルゴリズムを考えてみる 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 のほうが倍近く遅い
◆再帰とテンプレート実体化のコスト• 再帰を途中で打ち切るアルゴリズムを考えてみる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)
◆再帰とテンプレート実体化のコスト• [回避するには]• インデックスアクセス可能かつ線形探索ならば常に  index_tuple イディオムを使う – (再帰のオーバーヘッドも深さ制限もない)
◆コーディング上のコスト• コンパイル時だとまともにデバックできない – constexpr をマクロにして on/off オフすれば実⾏時デバッグが   できる
◆コーディング上のコスト• 処理フローが増えるたび、別の関数を作って処理を委譲  しなければならない – ループ処理 – ⼀時オブジェクトに名前を付ける
◆コーディング上のコスト• 例) 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>
◆コーディング上のコスト• 実装関数が増殖する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>
◆コーディング上のコスト• 名前付けが酷い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>
◆コーディング上のコスト• [回避するには]• 次期 C++1x に期待する• 諦める
◆constexpr なライブラリ•   標準ライブラリ•   libstdc++ 独⾃拡張•   CEL - ConstExpr Library•   Sprout C++ Library
◆標準ライブラリ• <limits>   – numeric_limits• <utility>   – pair (デフォルトコンストラクタのみ)• <tuple>   – tuple (デフォルトコンストラクタのみ)• <bitset>   – bitset• <memory>   –   unique_ptr (デフォルトコンストラクタのみ)   –   shared_ptr (デフォルトコンストラクタのみ)   –   weak_ptr (デフォルトコンストラクタのみ)   –   enable_shared_from_this (デフォルトコンストラクタのみ)
◆標準ライブラリ• <ratio>   – ratio• <chrono>   –   各種演算   –   duration_values   –   duration   –   time_point• <string>   – char_traits• <array>   – array (size, max_size, empty のみ)• <iterator>   – istream_iterator (デフォルトコンストラクタのみ)   – istreambuf_iterator (デフォルトコンストラクタのみ)
◆標準ライブラリ• <complex>   – complex• <random>   – 各種⽣成器クラス (min, max のみ)• <regex>   – sub_match (デフォルトコンストラクタのみ)• <atomic>   – atomic (コンストラクタのみ)• <mutex>   – mutex (デフォルトコンストラクタのみ)
◆標準ライブラリ• 標準ライブラリの⽅針としては、「通常  の使⽤において、ほぼ⾃明にコンパイル  時処理にできる」ものだけを constexpr  指定しているようだ – bitset – ratio – chrono など• numeric_limits が使える⼦になった• 保守的な⽅針
◆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>•   <numeric>•   <cstdlib>•   <cstring>•   <iterator>    – 各種アルゴリズム (⾮ Mutating な部分)
◆CEL - ConstExpr Library• <cctype>   – ⽂字列判定• <functional>   – 関数オブジェクト• <array>   – array• <utility>   – pair
◆CEL - ConstExpr Library• 標準ライブラリの関数の「シグネチャの  変更なしに constexpr 化できるもの」を  ほぼ⼀通りカバーしている
◆Sprout C++ Library•   constexpr   ⽂字列•   constexpr   タプル•   constexpr   バリアント•   constexpr   アルゴリズム•   constexpr   範囲アルゴリズム•   constexpr   コンテナ操作•   constexpr   乱数•   constexpr   ハッシュ関数•   constexpr   UUID•   constexpr   構⽂解析•   constexpr   レイトレーシング
◆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" }
◆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>
◆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 を弄って変更  – あるいはユーザ側に最⼤⻑を指定させる
◆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 }
◆constexpr アルゴリズム• Sprout.Algorithm  – 主に Mutating sequence operations をカバーする• CEL と合わせれば STL のアルゴリズムはほぼ全てカ  バーできる• RandomAccessIterator に対しては index_tuple イ  ディオムで効率的に処理する  – ユーザ側で sprout::next/prev をオーバーロードすることで    InputIterator なども渡せる• それ以外の IteratorCategory は Variadic Function で  実装されている
◆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"; }
◆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";                                    ランダムシャッフル }
◆constexpr 乱数• Sprout.Random  – <random> と同じく様々な乱数⽣成器と分布を提供する• 関数型⾔語にならって、乱数⽣成器は「⽣成した値」  「次の状態の⽣成器」のペアを返す仕様• コンパイル時に取得できる値でシードに使えるものが  __DATA__ __FILE__ __LINE__ くらいしかないので  それを使う
◆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); }
◆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); }
◆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); }
◆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); }
◆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); }
◆constexpr 構⽂解析• Sprout.Weed  – Boost.Spirit.Qi のような Expression Template ベースの    EDSL 構⽂解析ライブラリ• Expression Template 技法では、処理が細かい単位に  分割されるので、constexpr 化しやすい• constexpr の導⼊によって、コンパイル時⽂字列処理が  相当⼿軽に出来るようになった• おそらく今後 constexpr 正規表現などのライブラリも  出てくると思われる
◆constexpr レイトレーシング• Sprout.Darkroom
◆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;
◆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)       )    );
◆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<>();                                                         カメラ                                                        レンダラ                                                      レイトレーサー
◆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);
◆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;        }     } }
◆constexpr レイトレーシング• Sprout.Darkroom  – metatrace という TMP ライブラリを元にした constexpr レイ    トレーサーライブラリ• レイトレーシングの基本的なアルゴリズムはとてもシン  プル• 視点から各ピクセルを通る光線を⾶ばして、ベクトルが  オブジェクトと衝突する部分の拡散光と反射光の成分を  得るだけ
◆次期 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() { }};                             先頭メンバ以外での初期化はできない
◆次期 C++1x に期待すること• ラムダ式を定数式扱いにtemplate<class F>constexpr auto call(F f) -> decltype(f()) { return f(); }call( []{ return 0; } );                           constexpr 関数にラムダ式を渡せない• new/delete を定数式に出来るように• 標準ライブラリを更に constexpr 化
◆constexpr 化できそうなもの• 正規表現   – Regex / Expressive• 汎⽤ Expression Template   – Proto• シリアライズ   – Serialization• 画像処理   – GIL• 数学関数   – Math.SpecialFunction• etc...
◆まとめ• constexpr で表現可能な応⽤範囲はとて  も広い• コーディング上の制約と落とし⽳は多い  ので気をつける• 数値計算が得意分野• constexpr で遊ぼう!
ご静聴ありがとう ございました

Recommended

PDF
中3女子が狂える本当に気持ちのいい constexpr
PDF
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
PDF
Constexpr 中3女子テクニック
PDF
C++ マルチスレッドプログラミング
PDF
ゲーム開発者のための C++11/C++14
PDF
ARM CPUにおけるSIMDを用いた高速計算入門
PDF
20分くらいでわかった気分になれるC++20コルーチン
 
PDF
明日使えないすごいビット演算
PPTX
競技プログラミングのためのC++入門
PDF
C++ マルチスレッド 入門
PDF
条件分岐とcmovとmaxps
PDF
マルチコアを用いた画像処理
PDF
すごい constexpr たのしくレイトレ!
PDF
組み込みでこそC++を使う10の理由
PDF
C++コミュニティーの中心でC++をDISる
PDF
プログラムを高速化する話
PDF
直交領域探索
PPTX
AVX-512(フォーマット)詳解
PDF
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
PDF
入門 シェル実装
PDF
オブジェクト指向の設計と実装の学び方のコツ
PPTX
RustによるGPUプログラミング環境
PPTX
async/await のしくみ
PPT
レイトレ空間構造入門
PDF
画像認識の初歩、SIFT,SURF特徴量
PDF
1076: CUDAデバッグ・プロファイリング入門
PPTX
画像処理ライブラリ OpenCV で 出来ること・出来ないこと
PDF
カスタムメモリマネージャと高速なメモリアロケータについて
 
PDF
C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)
PDF
リテラル文字列型までの道

More Related Content

PDF
中3女子が狂える本当に気持ちのいい constexpr
PDF
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
PDF
Constexpr 中3女子テクニック
PDF
C++ マルチスレッドプログラミング
PDF
ゲーム開発者のための C++11/C++14
PDF
ARM CPUにおけるSIMDを用いた高速計算入門
PDF
20分くらいでわかった気分になれるC++20コルーチン
 
PDF
明日使えないすごいビット演算
中3女子が狂える本当に気持ちのいい constexpr
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
Constexpr 中3女子テクニック
C++ マルチスレッドプログラミング
ゲーム開発者のための C++11/C++14
ARM CPUにおけるSIMDを用いた高速計算入門
20分くらいでわかった気分になれるC++20コルーチン
 
明日使えないすごいビット演算

What's hot

PPTX
競技プログラミングのためのC++入門
PDF
C++ マルチスレッド 入門
PDF
条件分岐とcmovとmaxps
PDF
マルチコアを用いた画像処理
PDF
すごい constexpr たのしくレイトレ!
PDF
組み込みでこそC++を使う10の理由
PDF
C++コミュニティーの中心でC++をDISる
PDF
プログラムを高速化する話
PDF
直交領域探索
PPTX
AVX-512(フォーマット)詳解
PDF
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
PDF
入門 シェル実装
PDF
オブジェクト指向の設計と実装の学び方のコツ
PPTX
RustによるGPUプログラミング環境
PPTX
async/await のしくみ
PPT
レイトレ空間構造入門
PDF
画像認識の初歩、SIFT,SURF特徴量
PDF
1076: CUDAデバッグ・プロファイリング入門
PPTX
画像処理ライブラリ OpenCV で 出来ること・出来ないこと
PDF
カスタムメモリマネージャと高速なメモリアロケータについて
 
競技プログラミングのためのC++入門
C++ マルチスレッド 入門
条件分岐とcmovとmaxps
マルチコアを用いた画像処理
すごい constexpr たのしくレイトレ!
組み込みでこそC++を使う10の理由
C++コミュニティーの中心でC++をDISる
プログラムを高速化する話
直交領域探索
AVX-512(フォーマット)詳解
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
入門 シェル実装
オブジェクト指向の設計と実装の学び方のコツ
RustによるGPUプログラミング環境
async/await のしくみ
レイトレ空間構造入門
画像認識の初歩、SIFT,SURF特徴量
1076: CUDAデバッグ・プロファイリング入門
画像処理ライブラリ OpenCV で 出来ること・出来ないこと
カスタムメモリマネージャと高速なメモリアロケータについて
 

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

PDF
C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)
PDF
リテラル文字列型までの道
PDF
Boost Fusion Library
PDF
Effective Modern C++ 勉強会#3 Item 15
PDF
C++ Template Metaprogramming
PDF
Boost Tour 1.50.0 All
PDF
Boost.Flyweight
PDF
Pfi Seminar 2010 1 7
PDF
C++勉強会in広島プレゼン資料
PDF
テンプレートメタプログラミング as 式
PDF
わんくま同盟大阪勉強会#61
PDF
What is template
PDF
boost tour 1.48.0 all
PDF
Boost tour 1_40_0
PDF
2011.12.10 関数型都市忘年会 発表資料「最近書いた、関数型言語と関連する?C++プログラムの紹介」
PPTX
Lambda in template_final
PDF
C++ Template Meta Programming の紹介@社内勉強会
PDF
研究生のためのC++ no.2
PDF
Tr18015
PDF
C++ lecture-0
C++のSTLのコンテナ型を概観する @ Ohotech 特盛 #10(2014.8.30)
リテラル文字列型までの道
Boost Fusion Library
Effective Modern C++ 勉強会#3 Item 15
C++ Template Metaprogramming
Boost Tour 1.50.0 All
Boost.Flyweight
Pfi Seminar 2010 1 7
C++勉強会in広島プレゼン資料
テンプレートメタプログラミング as 式
わんくま同盟大阪勉強会#61
What is template
boost tour 1.48.0 all
Boost tour 1_40_0
2011.12.10 関数型都市忘年会 発表資料「最近書いた、関数型言語と関連する?C++プログラムの紹介」
Lambda in template_final
C++ Template Meta Programming の紹介@社内勉強会
研究生のためのC++ no.2
Tr18015
C++ lecture-0

中3女子でもわかる constexpr

  • 1.
    中3女子でもわかる! constexprBoost.勉強会 #7bolero_MURAKAMI2011/12/3
  • 2.
    ◆⾃⼰紹介• 名前 :村上 原野 (むらかみ げんや) @bolero_MURAKAMI, id:boleros• 棲息地: ⼤都会岡⼭• 仕事 : 猪⾵来美術館陶芸指導員 ・普段はやきものの修⾏をしたり、 縄⽂⼟器をつくったりしています ・趣味は constexpr です
  • 3.
    ◆⾃⼰紹介• 好きな C++11の機能: constexpr• 嫌いな C++11 のキーワード: constexpr• 次期 C++ で強化されてほしい機能: constexpr• 次期 C++ で消えてほしいキーワード: constexpr
  • 4.
    ◆⾃⼰紹介• 公開しているライブラリ: SproutC++ Library (constexpr ライブラリ) github.com/bolero-MURAKAMI/Sprout
  • 5.
    ◆アジェンダ•はじめに• constexpr とは?• constexpr 実装技法• constexpr の3⼤コスト• constexpr ライブラリを使ってみる• まとめ
  • 6.
    ◆constexpr とは?• N32907.1.5The 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.
    ◆constexpr とは?• N32907.1.5 constexpr 指定子 [dcl.constexpr] constexpr 指定子は、変数の定義、関数または関数テンプレートの宣言、 またはリテラル型(3.9)の静的データメンバの宣言に適用されるものとする。 (中略) [注:関数のパラメータは constexpr 宣言することはできない。]• C++11 で導⼊されたキーワードである• decl-specifier に分類される – (型修飾⼦ではない)
  • 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.
    ◆constexpr ⼊⾨• プログラミングの魔導書vol.2の江添さん の記事を読んでください• 読んでなかったら買ってください
  • 10.
    ◆C++ プログラミングのレイヤー C++03 [処理されるもの] [プログラマのすること] プリプロセス時の世界 プリプロセッサ (魔界) ソースコード メタプログラミング テンプレート 型 コンパイル時の世界 メタプログラミング(ライブラリアンが多数棲息) 定数式 実⾏時の世界 実⾏時オブジェクト (⼈間界) 通常の プログラミング
  • 11.
    ◆C++ プログラミングのレイヤー C++03 [処理されるもの] [プログラマのすること] プリプロセス時の世界 プリプロセッサ (魔界) ソースコード メタプログラミング テンプレート 型 コンパイル時の世界 メタプログラミング(ライブラリアンが多数棲息) 定数式 どっちも「値」を 求めるのは同じでも…… わたしたち 離ればなれね…… 実⾏時の世界 実⾏時オブジェクト (⼈間界) 通常の プログラミング
  • 12.
    ◆C++ プログラミングのレイヤー C++11 [処理されるもの] [プログラマのすること] プリプロセス時の世界 プリプロセッサ (魔界) ソースコード メタプログラミング テンプレート それ constexpr 型 で出来るよ! コンパイル時の世界 メタプログラミング(ライブラリアンが多数棲息) 定数式 抱いて!! constexpr キャーカッコイー!! 実⾏時の世界 実⾏時オブジェクト (⼈間界) 通常の プログラミング
  • 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.
    ◆定数も⾮定数も 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.
    ◆コンパイル時⽂字列• TMP でやってみるtypedefmpl::string<’Hell’, ’o, wo’, ’rld! ’> hello_t; – 書きづらい(醜い) – 遅い – 制限が多い – 型の領域だけでは無理がある
  • 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.
    ◆コンパイル時⽂字列• こんなことも出来るよ! – 例) コンパイル時⽂字列解析 (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.
    ◆コンパイル時⽂字列• こんなことも出来るよ! – 例) コンパイル時⽂字列解析 (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.
    ◆constexpr で扱えるデータ [リテラル型] [スカラ型] [リテラル型の配列] [算術型] LiteralType [N] [整数型] int, unsigned int, char, ... [リテラル型への参照] [浮動⼩数点型] float, double, ... LiteralType const& [ポインタ型] [ポインタ] int const*, int (*)(void), ... 特定の条件を満たす [メンバポインタ] int T::*, int (T::*)(void), ... ユーザ定義クラス [列挙型] enum
  • 20.
    ◆リテラル型クラスの条件• コンパイラの要求 –trivial コピーコンストラクタを持つ – ⾮ trivial ムーブコンストラクタを持たない – trivial デストラクタを持つ – trivial デフォルトコンストラクタか、コピーでもムーブでもな い constexpr コンストラクタを持つ – ⾮ static データメンバと基本クラスは、全てリテラル型である
  • 21.
    ◆リテラル型クラスの条件• プログラマの「べからず」 –仮想関数や仮想基底クラスを書かない – ユーザ定義コピーコンストラクタを書かない • (delete もしない) – ユーザ定義ムーブコンストラクタを書かない • (delete するのはよい) – ユーザ定義デストラクタを書かない – ユーザ定義デフォルトコンストラクタを書くなら原則 constexpr にする • (デフォルトコンストラクタが trivial でも constexpr でもない場 合、別の constexpr コンストラクタを書く) – リテラル型以外の⾮ static データメンバや基底クラスを使わな い
  • 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.
    ◆リテラル型クラスの条件• リテラル型になれない例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.
    ◆リテラル型クラスの条件• リテラル型になれない例struct NonLiteralType{private: リテラル型以外のデータメンバは NG! NonLiteralDataType data_member_;};
  • 25.
    ◆関数を constexpr 化する• 条件分岐• ループ• ローカル変数• エラー通知• シグネチャの constexpr 化• 配列操作 (Variadic function)• 配列操作 (index_tuple イディオム)
  • 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.
    ◆ループ• ⾮ 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.
    ◆ループ• ⾮ 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.
    ◆ローカル変数• ⾮ 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.
    ◆エラー通知• ⾮ 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.
    ◆エラー通知• ⾮ 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.
    ◆シグネチャの 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.
    ◆シグネチャの 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.
    ◆配列操作 (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.
    ◆配列操作 (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.
    ◆配列操作 (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.
    ◆配列操作 (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.
    ◆配列操作 (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.
    ◆配列操作 (Variadic function)•Variadic function の再帰はテンプレート実体化の無限 再帰になりやすいので注意する• そうなるケースでは再帰条件を SFINAE にする• (static if ほしいです)
  • 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.
    ◆配列操作 (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.
    ◆配列操作 (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.
    ◆配列操作 (index_tuple idiom)•index_tuple イディオムは、インデックスアクセス可能 なデータ構造⼀般に適⽤できる – 配列 – タプル – ランダムアクセスイテレータ – 型リスト• constexpr に限らず、通常の関数や TMP にも同じよう に応⽤できる• ただしインデックスアクセス不可なデータ構造には適⽤ できない – ⾮ランダムアクセスなイテレータなど
  • 44.
    ◆クラスを constexpr 化する•コンストラクタで処理を委譲• 状態を書き換えるメンバ関数の constexpr 化
  • 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.
    ◆コンストラクタで処理を委譲• Delegating constructortemplate<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.
    ◆コンストラクタで処理を委譲• C++03 Delegatingconstructor 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.
    ◆状態を書き換えるメンバ関数• ⾮ 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.
    ◆状態を書き換えるメンバ関数• ⾮ constexprクラス struct random_generator { unsigned int operator()(); 状態を書き換え、 }; かつ処理結果を返す ↓ 処理結果と「次の状態」の タプルを返す• constexprクラス struct random_generator { constexpr pair<unsigned int, random_generator> operator()() const; };
  • 50.
    ◆constexpr の3⼤コスト• オブジェクトコピーのコスト•再帰とテンプレートインスタンス化のコ スト• コーディング上のコスト (⼈間のコスト)
  • 51.
    ◆オブジェクトコピーのコスト• 配列の2つの要素の swapを考えてみる – ⾮ constexpr の場合 (元の配列を書換え)⾼々1回のコピーと 2回の代⼊(またはムーヴ) – constexpr の場合 (別の配列をつくる) 常に全要素の コピー• 単なる要素の交換に線形時間 O(n) を要する
  • 52.
    ◆オブジェクトコピーのコスト• 状態を書き換えるメンバ関数の呼出しを考えてみる constexprarray<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.
    ◆オブジェクトコピーのコスト• [回避するには]• 状態を変更する呼出しは可能な限り少なくする – (1つの関数で済ませる)• Variadic function や index_tuple イディオムを活⽤し てオブジェクトコピーが⾏われない処理を書く
  • 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.
    ◆再帰とテンプレート実体化のコスト• 再帰を途中で打ち切るアルゴリズムを考えてみる constexprauto 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.
    ◆再帰とテンプレート実体化のコスト• 再帰を途中で打ち切るアルゴリズムを考えてみるconstexpr autosrc_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.
  • 58.
    ◆コーディング上のコスト• コンパイル時だとまともにデバックできない –constexpr をマクロにして on/off オフすれば実⾏時デバッグが できる
  • 59.
    ◆コーディング上のコスト• 処理フローが増えるたび、別の関数を作って処理を委譲しなければならない – ループ処理 – ⼀時オブジェクトに名前を付ける
  • 60.
    ◆コーディング上のコスト• 例) constexpruniform_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.
    ◆コーディング上のコスト• 実装関数が増殖する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.
    ◆コーディング上のコスト• 名前付けが酷い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.
  • 64.
    ◆constexpr なライブラリ• 標準ライブラリ• libstdc++ 独⾃拡張• CEL - ConstExpr Library• Sprout C++ Library
  • 65.
    ◆標準ライブラリ• <limits> – numeric_limits• <utility> – pair (デフォルトコンストラクタのみ)• <tuple> – tuple (デフォルトコンストラクタのみ)• <bitset> – bitset• <memory> – unique_ptr (デフォルトコンストラクタのみ) – shared_ptr (デフォルトコンストラクタのみ) – weak_ptr (デフォルトコンストラクタのみ) – enable_shared_from_this (デフォルトコンストラクタのみ)
  • 66.
    ◆標準ライブラリ• <ratio> – ratio• <chrono> – 各種演算 – duration_values – duration – time_point• <string> – char_traits• <array> – array (size, max_size, empty のみ)• <iterator> – istream_iterator (デフォルトコンストラクタのみ) – istreambuf_iterator (デフォルトコンストラクタのみ)
  • 67.
    ◆標準ライブラリ• <complex> – complex• <random> – 各種⽣成器クラス (min, max のみ)• <regex> – sub_match (デフォルトコンストラクタのみ)• <atomic> – atomic (コンストラクタのみ)• <mutex> – mutex (デフォルトコンストラクタのみ)
  • 68.
    ◆標準ライブラリ• 標準ライブラリの⽅針としては、「通常の使⽤において、ほぼ⾃明にコンパイル 時処理にできる」ものだけを constexpr 指定しているようだ – bitset – ratio – chrono など• numeric_limits が使える⼦になった• 保守的な⽅針
  • 69.
    ◆libstdc++ 独⾃拡張 (GCC 4.7 experimental)• <cmath> – 各種関数• <tuple> – tuple• <utility> – forward, move
  • 70.
    ◆libstdc++ 独⾃拡張• <cmath>が constexpr 化されてるのが ⼤きい – これに依存する前提なら多くの数学計算の constexpr 化が楽になる• なんで <array> が constexpr じゃない のか……
  • 71.
    ◆CEL - ConstExprLibrary• 作者: RiSK (@sscrisk) さん – https://github.com/sscrisk/CEL---ConstExpr-Library• <algorithm>• <numeric>• <cstdlib>• <cstring>• <iterator> – 各種アルゴリズム (⾮ Mutating な部分)
  • 72.
    ◆CEL - ConstExprLibrary• <cctype> – ⽂字列判定• <functional> – 関数オブジェクト• <array> – array• <utility> – pair
  • 73.
    ◆CEL - ConstExprLibrary• 標準ライブラリの関数の「シグネチャの 変更なしに constexpr 化できるもの」を ほぼ⼀通りカバーしている
  • 74.
    ◆Sprout C++ Library• constexpr ⽂字列• constexpr タプル• constexpr バリアント• constexpr アルゴリズム• constexpr 範囲アルゴリズム• constexpr コンテナ操作• constexpr 乱数• constexpr ハッシュ関数• constexpr UUID• constexpr 構⽂解析• constexpr レイトレーシング
  • 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.
    ◆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.
    ◆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.
    ◆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.
    ◆constexpr アルゴリズム• Sprout.Algorithm – 主に Mutating sequence operations をカバーする• CEL と合わせれば STL のアルゴリズムはほぼ全てカ バーできる• RandomAccessIterator に対しては index_tuple イ ディオムで効率的に処理する – ユーザ側で sprout::next/prev をオーバーロードすることで InputIterator なども渡せる• それ以外の IteratorCategory は Variadic Function で 実装されている
  • 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.
    ◆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.
    ◆constexpr 乱数• Sprout.Random – <random> と同じく様々な乱数⽣成器と分布を提供する• 関数型⾔語にならって、乱数⽣成器は「⽣成した値」 「次の状態の⽣成器」のペアを返す仕様• コンパイル時に取得できる値でシードに使えるものが __DATA__ __FILE__ __LINE__ くらいしかないので それを使う
  • 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.
    ◆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.
    ◆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.
    ◆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.
    ◆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.
    ◆constexpr 構⽂解析• Sprout.Weed – Boost.Spirit.Qi のような Expression Template ベースの EDSL 構⽂解析ライブラリ• Expression Template 技法では、処理が細かい単位に 分割されるので、constexpr 化しやすい• constexpr の導⼊によって、コンパイル時⽂字列処理が 相当⼿軽に出来るようになった• おそらく今後 constexpr 正規表現などのライブラリも 出てくると思われる
  • 89.
  • 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.
    ◆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.
    ◆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.
    ◆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.
    ◆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.
    ◆constexpr レイトレーシング• Sprout.Darkroom – metatrace という TMP ライブラリを元にした constexpr レイ トレーサーライブラリ• レイトレーシングの基本的なアルゴリズムはとてもシン プル• 視点から各ピクセルを通る光線を⾶ばして、ベクトルが オブジェクトと衝突する部分の拡散光と反射光の成分を 得るだけ
  • 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.
    ◆次期 C++1x に期待すること•ラムダ式を定数式扱いにtemplate<class F>constexpr auto call(F f) -> decltype(f()) { return f(); }call( []{ return 0; } ); constexpr 関数にラムダ式を渡せない• new/delete を定数式に出来るように• 標準ライブラリを更に constexpr 化
  • 98.
    ◆constexpr 化できそうなもの• 正規表現 – Regex / Expressive• 汎⽤ Expression Template – Proto• シリアライズ – Serialization• 画像処理 – GIL• 数学関数 – Math.SpecialFunction• etc...
  • 99.
    ◆まとめ• constexpr で表現可能な応⽤範囲はとて も広い• コーディング上の制約と落とし⽳は多い ので気をつける• 数値計算が得意分野• constexpr で遊ぼう!
  • 100.

[8]ページ先頭

©2009-2025 Movatter.jp