SlideShare una empresa de Scribd logo
1 de 100
Descargar para leer sin conexión
中3女子でもわかる!
                  constexpr

Boost.勉強会 #7
bolero_MURAKAMI
2011/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 とは?
• N3290
7.1.5 The constexpr specifier [dcl.constexpr]

1 The constexpr specifier shall be applied only to the definition of a variable,
the declaration of a function or function template, or the declaration of a
static data member of a literal type (3.9). If any declaration of a function or
function template has constexpr specifier, then all its declarations shall
contain the constexpr specifier. [ Note: An explicit specialization can differ
from the template declaration with respect to the constexpr specifier. -end
note ] [ Note: Function parameters cannot be declared constexpr.-end note ]
◆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, BaseUnsigned
brange, BaseUnsigned bucket_size);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_3_1_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange,
BaseUnsigned bucket_size, BaseUnsigned result);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned
brange, BaseUnsigned bucket_size);
                                                               これは宣⾔だけ書き出して⼀部省略しているので
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>

                 引数コピペの嵐                                            実際のコードはもっと酷い
constexpr result<T, Engine> generate_uniform_int_true_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange);

                                                         元々はたった
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2_4(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType

                                                       2 個の関数だった
result, RangeType result_increment);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType

                             18 個の実装関数
result, RangeType mult, RangeType result_increment);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned, typename Result>
constexpr result<T, Engine> generate_uniform_int_true_2_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType
result, RangeType mult, Result const& result_increment_base);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType
limit, RangeType result = RangeType(0), RangeType mult = RangeType(1));
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2_1_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned
brange, RangeType limit, RangeType result, RangeType mult);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType
limit, RangeType result, RangeType mult);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange);
template<typename Engine, typename T, typename BaseResult>
constexpr result<T, Engine> generate_uniform_int_true_1_1(random_result<Engine> const& rnd, T min_value, BaseResult bmin);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_1(Engine const& eng, T min_value, T max_value, RangeType range, BaseResult bmin, BaseUnsigned brange);
template<typename Engine, typename T>
constexpr result<T, Engine> generate_uniform_int(Engine const& eng, T min_value, T max_value, std::true_type);
template<typename Engine, typename T, typename Result>
constexpr result<T, Engine> generate_uniform_int_false_1(Result const& rnd);
template<typename Engine, typename T>
◆コーディング上のコスト
• 名前付けが酷い
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned
brange, BaseUnsigned bucket_size);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_3_1_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange,

                                             generate_uniform_int_true_2_1 とか
BaseUnsigned bucket_size, BaseUnsigned result);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned
brange, BaseUnsigned bucket_size);           generate_uniform_int_true_2_2 とか
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
                                             generate_uniform_int_true_2_3 ...
constexpr result<T, Engine> generate_uniform_int_true_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2_4(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType
result, RangeType result_increment);                          そもそも意味論でなく単に処理フローで
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
                                                                 分割しているだけなので、
constexpr result<T, Engine> generate_uniform_int_true_2_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType
result, RangeType mult, RangeType result_increment);
                                                             各関数に意味のある名前を付けるのが難しい
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned, typename Result>
constexpr result<T, Engine> generate_uniform_int_true_2_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType
result, RangeType mult, Result const& result_increment_base);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType
limit, RangeType result = RangeType(0), RangeType mult = RangeType(1));
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2_1_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned
                                      constexpr ラムダ式があれば
brange, RangeType limit, RangeType result, RangeType mult);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
                                       救われる(かもしれない)
constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType
limit, RangeType result, RangeType mult);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange);
template<typename Engine, typename T, typename BaseResult>
constexpr result<T, Engine> generate_uniform_int_true_1_1(random_result<Engine> const& rnd, T min_value, BaseResult bmin);
template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned>
constexpr result<T, Engine> generate_uniform_int_true_1(Engine const& eng, T min_value, T max_value, RangeType range, BaseResult bmin, BaseUnsigned brange);
template<typename Engine, typename T>
constexpr result<T, Engine> generate_uniform_int(Engine const& eng, T min_value, T max_value, std::true_type);
template<typename Engine, typename T, typename Result>
constexpr result<T, Engine> generate_uniform_int_false_1(Result const& rnd);
template<typename Engine, typename T>
◆コーディング上のコスト
• [回避するには]
• 次期 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 で遊ぼう!
ご静聴ありがとう
 ございました

Más contenido relacionado

La actualidad más candente

今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 TipsTakaaki Suzuki
 
.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理KageShiron
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14Ryo Suzuki
 
中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexpr中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexprGenya Murakami
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門Norishige Fukushima
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由kikairoya
 
20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチンyohhoy
 
なぜなにリアルタイムレンダリング
なぜなにリアルタイムレンダリングなぜなにリアルタイムレンダリング
なぜなにリアルタイムレンダリングSatoshi Kodaira
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるHideyuki Tanaka
 
高速な倍精度指数関数expの実装
高速な倍精度指数関数expの実装高速な倍精度指数関数expの実装
高速な倍精度指数関数expの実装MITSUNARI Shigeo
 
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しようUnity Technologies Japan K.K.
 
SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介MITSUNARI Shigeo
 
最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)Akihiko Matuura
 
Boost.Preprocessorでプログラミングしましょう
Boost.PreprocessorでプログラミングしましょうBoost.Preprocessorでプログラミングしましょう
Boost.Preprocessorでプログラミングしましょうdigitalghost
 
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)Takeshi Yamamuro
 
async/await のしくみ
async/await のしくみasync/await のしくみ
async/await のしくみ信之 岩永
 
新しい並列for構文のご提案
新しい並列for構文のご提案新しい並列for構文のご提案
新しい並列for構文のご提案yohhoy
 

La actualidad más candente (20)

今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips
 
.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理.NET Core 3.0時代のメモリ管理
.NET Core 3.0時代のメモリ管理
 
C++ マルチスレッド 入門
C++ マルチスレッド 入門C++ マルチスレッド 入門
C++ マルチスレッド 入門
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14
 
中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexpr中3女子が狂える本当に気持ちのいい constexpr
中3女子が狂える本当に気持ちのいい constexpr
 
明日使えないすごいビット演算
明日使えないすごいビット演算明日使えないすごいビット演算
明日使えないすごいビット演算
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
 
20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン
 
なぜなにリアルタイムレンダリング
なぜなにリアルタイムレンダリングなぜなにリアルタイムレンダリング
なぜなにリアルタイムレンダリング
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISる
 
高速な倍精度指数関数expの実装
高速な倍精度指数関数expの実装高速な倍精度指数関数expの実装
高速な倍精度指数関数expの実装
 
llvm入門
llvm入門llvm入門
llvm入門
 
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
 
SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介
 
最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)
 
Boost.Preprocessorでプログラミングしましょう
Boost.PreprocessorでプログラミングしましょうBoost.Preprocessorでプログラミングしましょう
Boost.Preprocessorでプログラミングしましょう
 
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
LLVMで遊ぶ(整数圧縮とか、x86向けの自動ベクトル化とか)
 
async/await のしくみ
async/await のしくみasync/await のしくみ
async/await のしくみ
 
新しい並列for構文のご提案
新しい並列for構文のご提案新しい並列for構文のご提案
新しい並列for構文のご提案
 

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

リテラル文字列型までの道
リテラル文字列型までの道リテラル文字列型までの道
リテラル文字列型までの道Satoshi Sato
 
C++0x 言語の未来を語る
C++0x 言語の未来を語るC++0x 言語の未来を語る
C++0x 言語の未来を語るAkira Takahashi
 
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜Hiromi Ishii
 
とあるFlashの自動生成
とあるFlashの自動生成とあるFlashの自動生成
とあるFlashの自動生成Akineko Shimizu
 
T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門伸男 伊藤
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Ransui Iso
 
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーNode.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーmganeko
 
C# 7.2 with .NET Core 2.1
C# 7.2 with .NET Core 2.1C# 7.2 with .NET Core 2.1
C# 7.2 with .NET Core 2.1信之 岩永
 
Boost.Flyweight
Boost.FlyweightBoost.Flyweight
Boost.Flyweightgintenlabo
 
わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61TATSUYA HAYAMIZU
 
C++によるソート入門
C++によるソート入門C++によるソート入門
C++によるソート入門AimingStudy
 
Rubyの御先祖CLUのお話(原本)
Rubyの御先祖CLUのお話(原本)Rubyの御先祖CLUのお話(原本)
Rubyの御先祖CLUのお話(原本)洋史 東平
 
闇魔術を触ってみた
闇魔術を触ってみた闇魔術を触ってみた
闇魔術を触ってみたSatoshi Sato
 
Effective Modern C++ 勉強会#3 Item 15
Effective Modern C++ 勉強会#3 Item 15Effective Modern C++ 勉強会#3 Item 15
Effective Modern C++ 勉強会#3 Item 15Mitsuru Kariya
 
すごいHaskell読書会 第7章 (前編)
すごいHaskell読書会 第7章 (前編)すごいHaskell読書会 第7章 (前編)
すごいHaskell読書会 第7章 (前編)Suguru Hamazaki
 
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2Ransui Iso
 
第2回勉強会スライド
第2回勉強会スライド第2回勉強会スライド
第2回勉強会スライドkoturn 0;
 
関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)啓 小笠原
 
.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#信之 岩永
 

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

リテラル文字列型までの道
リテラル文字列型までの道リテラル文字列型までの道
リテラル文字列型までの道
 
C++0x 言語の未来を語る
C++0x 言語の未来を語るC++0x 言語の未来を語る
C++0x 言語の未来を語る
 
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
 
とあるFlashの自動生成
とあるFlashの自動生成とあるFlashの自動生成
とあるFlashの自動生成
 
T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門
 
Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3Lisp Tutorial for Pythonista : Day 3
Lisp Tutorial for Pythonista : Day 3
 
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーNode.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
 
C# 7.2 with .NET Core 2.1
C# 7.2 with .NET Core 2.1C# 7.2 with .NET Core 2.1
C# 7.2 with .NET Core 2.1
 
Boost.Flyweight
Boost.FlyweightBoost.Flyweight
Boost.Flyweight
 
わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61
 
C++によるソート入門
C++によるソート入門C++によるソート入門
C++によるソート入門
 
Rubyの御先祖CLUのお話(原本)
Rubyの御先祖CLUのお話(原本)Rubyの御先祖CLUのお話(原本)
Rubyの御先祖CLUのお話(原本)
 
闇魔術を触ってみた
闇魔術を触ってみた闇魔術を触ってみた
闇魔術を触ってみた
 
Effective Modern C++ 勉強会#3 Item 15
Effective Modern C++ 勉強会#3 Item 15Effective Modern C++ 勉強会#3 Item 15
Effective Modern C++ 勉強会#3 Item 15
 
すごいHaskell読書会 第7章 (前編)
すごいHaskell読書会 第7章 (前編)すごいHaskell読書会 第7章 (前編)
すごいHaskell読書会 第7章 (前編)
 
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
 
第2回勉強会スライド
第2回勉強会スライド第2回勉強会スライド
第2回勉強会スライド
 
関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)
 
Boost Tour 1.50.0 All
Boost Tour 1.50.0 AllBoost Tour 1.50.0 All
Boost Tour 1.50.0 All
 
.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#
 

Último

[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略Ryo Sasaki
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNetToru Tamaki
 
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Yuma Ohgami
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A surveyToru Tamaki
 
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)Hiroki Ichikura
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...Toru Tamaki
 
SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものですiPride Co., Ltd.
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdftaisei2219
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Danieldanielhu54
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムsugiuralab
 

Último (10)

[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet
 
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey
 
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
 
SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものです
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdf
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Daniel
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システム
 

中3女子でもわかる constexpr

  • 1. 中3女子でもわかる! constexpr Boost.勉強会 #7 bolero_MURAKAMI 2011/12/3
  • 2. ◆⾃⼰紹介 • 名前 : 村上 原野 (むらかみ げんや) @bolero_MURAKAMI, id:boleros • 棲息地: ⼤都会岡⼭ • 仕事 : 猪⾵来美術館陶芸指導員 ・普段はやきものの修⾏をしたり、 縄⽂⼟器をつくったりしています ・趣味は constexpr です
  • 3. ◆⾃⼰紹介 • 好きな C++11 の機能: constexpr • 嫌いな C++11 のキーワード: constexpr • 次期 C++ で強化されてほしい機能: constexpr • 次期 C++ で消えてほしいキーワード: constexpr
  • 4. ◆⾃⼰紹介 • 公開しているライブラリ: Sprout C++ Library (constexpr ライブラリ) github.com/bolero-MURAKAMI/Sprout
  • 5. ◆アジェンダ • はじめに • constexpr とは? • constexpr 実装技法 • constexpr の3⼤コスト • constexpr ライブラリを使ってみる • まとめ
  • 6. ◆constexpr とは? • N3290 7.1.5 The constexpr specifier [dcl.constexpr] 1 The constexpr specifier shall be applied only to the definition of a variable, the declaration of a function or function template, or the declaration of a static data member of a literal type (3.9). If any declaration of a function or function template has constexpr specifier, then all its declarations shall contain the constexpr specifier. [ Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier. -end note ] [ Note: Function parameters cannot be declared constexpr.-end note ]
  • 7. ◆constexpr とは? • N3290 7.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 でやってみる typedef mpl::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 constructor template<class T> struct minmax_t { まず minmax を求めてから template<class Iter> 別のコンストラクタに処理を丸投げ constexpr minmax_t(Iter first, Iter last) : minmax_t(minmax_element(first, last)) {} private: template<class Iter> constexpr minmax_t(pair<Iter, Iter> const& minmax) : min_(*minmax.first) , max_(*minmax.second) minmax を min と max に振り分け {} T min_, max_; };
  • 47. ◆コンストラクタで処理を委譲 • C++03 Delegating constructor workaround template<class T> struct minmax_t_impl { protected: template<class Iter> constexpr minmax_t_impl(pair<Iter, Iter> const& minmax) : min_(*minmax.first) , max_(*minmax.second) 実装⽤クラスで minmax を {} min と max に振り分け T min_, max_; }; private 継承 template<class T> struct minmax_t : private minmax_t_impl<T> { template<class Iter> 実装⽤クラスに処理を丸投げ constexpr minmax_t(Iter first, Iter last) : minmax_t_impl<T>(minmax_element(first, last)) {} private: using minmax_t_impl<T>::min_; using minmax_t_impl<T>::max_; }; 実装⽤クラスのメンバを using
  • 48. ◆状態を書き換えるメンバ関数 • ⾮ 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. ◆オブジェクトコピーのコスト • 状態を書き換えるメンバ関数の呼出しを考えてみる constexpr array<unsigned char, N> src = { /*...*/ }; sha1.process_byte(src[0]) .process_byte(src[1]) 計算量= /*...*/ (計算毎+コピー毎)×要素数 .process_byte(src[N-1]); sha1.process_block(src.begin(), src.end()); 計算量=計算毎×要素数+コピー • 状態を書き換えるメンバ関数を constexpr にすると、 (オブジェクトコピー×呼出し回数)の計算量が余計に かかる
  • 53. ◆オブジェクトコピーのコスト • [回避するには] • 状態を変更する呼出しは可能な限り少なくする – (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. ◆再帰とテンプレート実体化のコスト • 再帰を途中で打ち切るアルゴリズムを考えてみる constexpr auto src_1 = array<int, 10>{{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }}; constexpr auto copied_1 = copy_to_find(src_1, 5); 再帰は 5 まで 要素数が 20 constexpr auto src_2 = array<unsigned, 20>{{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }}; constexpr auto copied_2 = copy_to_find(src_2, 5); 再帰は 5 まで • copied_1 の計算量は copied_2 と変わらないはず – ところが、そうはならない (gcc 4.7) – copied_2 のほうが倍近く遅い
  • 56. ◆再帰とテンプレート実体化のコスト • 再帰を途中で打ち切るアルゴリズムを考えてみる 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)
  • 58. ◆コーディング上のコスト • コンパイル時だとまともにデバックできない – constexpr をマクロにして on/off オフすれば実⾏時デバッグが できる
  • 59. ◆コーディング上のコスト • 処理フローが増えるたび、別の関数を作って処理を委譲 しなければならない – ループ処理 – ⼀時オブジェクトに名前を付ける
  • 60. ◆コーディング上のコスト • 例) constexpr uniform_int_distribution の実装 template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size, BaseUnsigned result); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_4(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType result_increment); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType mult, RangeType result_increment); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned, typename Result> constexpr result<T, Engine> generate_uniform_int_true_2_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType mult, Result const& result_increment_base); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result = RangeType(0), RangeType mult = RangeType(1)); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename BaseResult> constexpr result<T, Engine> generate_uniform_int_true_1_1(random_result<Engine> const& rnd, T min_value, BaseResult bmin); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_1(Engine const& eng, T min_value, T max_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T> constexpr result<T, Engine> generate_uniform_int(Engine const& eng, T min_value, T max_value, std::true_type); template<typename Engine, typename T, typename Result> constexpr result<T, Engine> generate_uniform_int_false_1(Result const& rnd); template<typename Engine, typename T>
  • 61. ◆コーディング上のコスト • 実装関数が増殖する template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size, BaseUnsigned result); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); これは宣⾔だけ書き出して⼀部省略しているので template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> 引数コピペの嵐 実際のコードはもっと酷い constexpr result<T, Engine> generate_uniform_int_true_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); 元々はたった template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_4(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType 2 個の関数だった result, RangeType result_increment); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType 18 個の実装関数 result, RangeType mult, RangeType result_increment); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned, typename Result> constexpr result<T, Engine> generate_uniform_int_true_2_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType mult, Result const& result_increment_base); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result = RangeType(0), RangeType mult = RangeType(1)); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename BaseResult> constexpr result<T, Engine> generate_uniform_int_true_1_1(random_result<Engine> const& rnd, T min_value, BaseResult bmin); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_1(Engine const& eng, T min_value, T max_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T> constexpr result<T, Engine> generate_uniform_int(Engine const& eng, T min_value, T max_value, std::true_type); template<typename Engine, typename T, typename Result> constexpr result<T, Engine> generate_uniform_int_false_1(Result const& rnd); template<typename Engine, typename T>
  • 62. ◆コーディング上のコスト • 名前付けが酷い template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, generate_uniform_int_true_2_1 とか BaseUnsigned bucket_size, BaseUnsigned result); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_3_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, BaseUnsigned bucket_size); generate_uniform_int_true_2_2 とか template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> generate_uniform_int_true_2_3 ... constexpr result<T, Engine> generate_uniform_int_true_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_4(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType result_increment); そもそも意味論でなく単に処理フローで template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> 分割しているだけなので、 constexpr result<T, Engine> generate_uniform_int_true_2_3(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType mult, RangeType result_increment); 各関数に意味のある名前を付けるのが難しい template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned, typename Result> constexpr result<T, Engine> generate_uniform_int_true_2_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType result, RangeType mult, Result const& result_increment_base); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result = RangeType(0), RangeType mult = RangeType(1)); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2_1_1(random_result<Engine> const& rnd, T min_value, RangeType range, BaseResult bmin, BaseUnsigned constexpr ラムダ式があれば brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> 救われる(かもしれない) constexpr result<T, Engine> generate_uniform_int_true_2_1(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange, RangeType limit, RangeType result, RangeType mult); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_2(Engine const& eng, T min_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T, typename BaseResult> constexpr result<T, Engine> generate_uniform_int_true_1_1(random_result<Engine> const& rnd, T min_value, BaseResult bmin); template<typename Engine, typename T, typename RangeType, typename BaseResult, typename BaseUnsigned> constexpr result<T, Engine> generate_uniform_int_true_1(Engine const& eng, T min_value, T max_value, RangeType range, BaseResult bmin, BaseUnsigned brange); template<typename Engine, typename T> constexpr result<T, Engine> generate_uniform_int(Engine const& eng, T min_value, T max_value, std::true_type); template<typename Engine, typename T, typename Result> constexpr result<T, Engine> generate_uniform_int_false_1(Result const& rnd); template<typename Engine, typename T>
  • 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 - ConstExpr Library • 作者: RiSK (@sscrisk) さん – https://github.com/sscrisk/CEL---ConstExpr-Library • <algorithm> • <numeric> • <cstdlib> • <cstring> • <iterator> – 各種アルゴリズム (⾮ Mutating な部分)
  • 72. ◆CEL - ConstExpr Library • <cctype> – ⽂字列判定 • <functional> – 関数オブジェクト • <array> – array • <utility> – pair
  • 73. ◆CEL - ConstExpr Library • 標準ライブラリの関数の「シグネチャの 変更なしに 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 正規表現などのライブラリも 出てくると思われる
  • 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 で遊ぼう!