中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 で遊ぼう!
ご静聴ありがとう
 ございました

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