中3女子でもわかる constexpr
DESCRIPTION
Boost.勉強会 #7 中3女子でもわかる constexprTRANSCRIPT
中3女子でもわかる! constexpr
Boost.勉強会
#7bolero_MURAKAMI2011/12/3
◆⾃⼰紹介•
名前
:
村上
原野
(むらかみ
げんや)
@bolero_MURAKAMI, id:boleros
•
棲息地:
⼤都会岡⼭
•
仕事
:
猪⾵来美術館陶芸指導員・普段はやきものの修⾏をしたり、
縄⽂⼟器をつくったりしています・趣味は
constexpr です
◆⾃⼰紹介•
好きな
C++11 の機能:
constexpr•
嫌いな
C++11 のキーワード:
constexpr•
次期
C++ で強化されてほしい機能:
constexpr•
次期
C++ で消えてほしいキーワード:
constexpr
◆⾃⼰紹介•
公開しているライブラリ:
Sprout C++ Library (constexpr ライブラリ)github.com/bolero-MURAKAMI/Sprout
◆アジェンダ•
はじめに
•
constexpr とは?•
constexpr 実装技法
•
constexpr の3⼤コスト•
constexpr ライブラリを使ってみる
•
まとめ
◆constexpr とは?•
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
•
C++11 で導⼊されたキーワードである
•
decl-specifier に分類される–
(型修飾⼦ではない)
7.1.5 constexpr 指定子 [dcl.constexpr]
constexpr 指定子は、変数の定義、関数または関数テンプレートの宣言、またはリテラル型(3.9)の静的データメンバの宣言に適用されるものとする。
(中略)[注:関数のパラメータは
constexpr 宣言することはできない。]
◆constexpr とは?•
constexpr 宣⾔された変数は、コンパイル時定数になる
•
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 int zero = 0; // constexpr 変数using zero_t = std::integral_constant<int, zero>; // テンプレートにも渡せる
◆constexpr ⼊⾨•
プログラミングの魔導書vol.2 の江添さん
の記事を読んでください•
読んでなかったら買ってください
◆C++ プログラミングのレイヤー
プリプロセス時の世界(魔界)
コンパイル時の世界(ライブラリアンが多数棲息)
実⾏時の世界(⼈間界)
[プログラマのすること][処理されるもの]
ソースコードプリプロセッサ
メタプログラミング
テンプレートメタプログラミング型
実⾏時オブジェクト
定数式
通常のプログラミング
C++03
◆C++ プログラミングのレイヤー
プリプロセス時の世界(魔界)
コンパイル時の世界(ライブラリアンが多数棲息)
実⾏時の世界(⼈間界)
[プログラマのすること][処理されるもの]
ソースコードプリプロセッサ
メタプログラミング
テンプレートメタプログラミング型
実⾏時オブジェクト
定数式
通常のプログラミング
どっちも「値」を求めるのは同じでも…… わたしたち
離ればなれね……
C++03
◆C++ プログラミングのレイヤー
プリプロセス時の世界(魔界)
コンパイル時の世界(ライブラリアンが多数棲息)
実⾏時の世界(⼈間界)
[プログラマのすること][処理されるもの]
constexpr
ソースコードプリプロセッサ
メタプログラミング
テンプレートメタプログラミング型
実⾏時オブジェクト
定数式
通常のプログラミング
それ
constexprで出来るよ!
抱いて!!
キャーカッコイー!!
C++11
◆定数も⾮定数も
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 で定数値を計算する
•
関数で実⾏時に値を計算する
↓•
どっちも
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);constexpr auto compiletime_value = max(a, b);
typedef typename mpl::max<A, B>::type value_t;constexpr auto compiletime_value = value_t::value;
auto runtime_value = std::max(a, b);
しかも
TMP より⾼速
◆コンパイル時⽂字列•
TMP でやってみる
–
書きづらい(醜い)–
遅い
–
制限が多い–
型の領域だけでは無理がある
typedef mpl::string<’Hell’, ’o, wo’, ’rld! ’> hello_t;
◆コンパイル時⽂字列•
TMP でやってみる
–
書きづらい(醜い)–
遅い
–
制限が多い–
型の領域だけでは無理がある
↓•
簡単さ。そう、constexpr ならね
#include <sprout/string.hpp>
constexpr auto hello = sprout::to_string(“hello, world!”);
typedef mpl::string<’Hell’, ’o, wo’, ’rld! ’> hello_t;
Sprout C++ Library のconstexpr string
⽂字列リテラルもそのまま使える
◆コンパイル時⽂字列•
こんなことも出来るよ!–
例) コンパイル時⽂字列解析
(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;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";}
UUID ⽂字列
ブラケット無しのUUID にマッチするパーサ
ブラケット無し/有り両⽅にマッチするようパーサを合成
パースを実⾏
最後以外全部コンパイル時に処理される
◆constexpr で扱えるデータ[リテラル型]
[スカラ型] [リテラル型の配列]LiteralType [N]
[リテラル型への参照]LiteralType const&
[算術型]
[整数型] int, unsigned int, char, ...
[浮動⼩数点型] float, double, ...
[ポインタ型]
[ポインタ] 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: virtual BaseType: NonLiteralBaseType
{ };
struct NonLiteralType {virtual MemberFunction() const;
};
struct NonLiteralType {constexpr LiteralType(LiteralType const&) { }LiteralType(LiteralType const&) = delete;
};
仮想基底クラスは
NG!
リテラル型以外の基底クラスは
NG!
仮想関数は
NG!
ユーザ定義コピーコンストラクタは
NG!
コピーコンストラクタの
delete も
NG!
◆リテラル型クラスの条件•
リテラル型になれない例
struct NonLiteralType {~LiteralType() { }
};
struct NonLiteralType {LiteralType() { }// constexpr explicit LiteralType(int) { }
};
struct NonLiteralType {constexpr LiteralType(LiteralType&&) { }// LiteralType(LiteralType&&) = delete;
};
ユーザ定義デストラクタは
NG!
ユーザ定義ムーヴコンストラクタは
NG!
ムーヴコンストラクタの
delete は
OK
デフォルトコンストラクタが⾮
constexpr の場合別の
constexpr コンストラクタがあれば
OK
◆リテラル型クラスの条件•
リテラル型になれない例
struct NonLiteralType {private:
NonLiteralDataType data_member_;};
リテラル型以外のデータメンバは
NG!
◆関数を
constexpr 化する•
条件分岐
•
ループ•
ローカル変数
•
エラー通知•
シグネチャの
constexpr 化
•
配列操作
(Variadic function)•
配列操作
(index_tuple イディオム)
◆条件分岐•
⾮ constexpr 関数
•
constexpr 関数
template<class T>T max(T const& a, T const& b) {
if (a < b) return b;else return a;
}
template<class T>constexpr T max(T const& a, T const& b) {
return (a < b) ? b : a;}
if ⽂↓
条件演算⼦
◆ループ•
⾮ constexpr 関数
•
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;
}
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 関数
•
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;
}
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 関数
•
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 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 関数
•
constexpr 関数
template<class T>T const* next(T const* p) {
if (p) return p + 1;else assert(0); // error
}
template<class T>constexpr T const* next(T const* p) {
return p ? p + 1: throw std::invalid_argument("p is nullptr");
}
assert / 実⾏時例外↓
例外
コンパイル時にはコンパイルエラー実⾏時には例外が投げられる
◆エラー通知•
⾮ constexpr 関数
•
constexpr 関数
template<class T>T const* next(T const* p) {
if (p) return p + 1;else assert(0); // error
}
template<class T>constexpr T const* next(T const* p) {
static_assert(p != 0, "p is nullptr"); // NG!return p + 1;
}
assert / 実⾏時例外↓
×
static_assert
p
が実⾏時引数のときp !=0
は⾮定数式
◆シグネチャの
constexpr 化•
⾮ constexpr 関数
•
constexpr 関数
template<class T>void inc(T& x) {
++x;}
template<class T>constexpr T inc(T const& x) {
return x + 1;}
返値
void →
値を返す
引数書換え
→
副作⽤無しに
◆シグネチャの
constexpr 化•
⾮ constexpr 関数
•
constexpr 関数
template<class Iter, class Expr, class Attr>bool parse(Iter& first, Iter last, Expr const& expr, Attr& attr);
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>using namespace sprout;
template<class T, size_t N>constexpr array<T, N> reverse(array<T, N> const& arr);
constexpr array
適⽤結果の配列を返す
◆配列操作
(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);}
配列要素のケツから次々Parameter pack の末尾に挿⼊
要素全て
Parameter pack に追加されるまで
◆配列操作
(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)? 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);}
sizeof...(Args) == N
のとき再帰終了する?
結果、テンプレートの実体化が無限再帰になる
「評価」はされないが「テンプレートの実体化」はされる
→
実体化の再帰は終了しない
◆配列操作
(Variadic function)•
reverse アルゴリズムを実装してみる
template<class T, size_t N, class... Args>constexpr typename enable_if<
(sizeof...(Args) >= N),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>constexpr typename enable_if<
(sizeof...(Args) < N),array<T, N>
>::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);}
SFINAE: ここで再帰終了
SFINAE: 再帰を続ける場合
再帰条件をSFINAE で書く
◆配列操作
(Variadic function)•
Variadic function の再帰はテンプレート実体化の無限
再帰になりやすいので注意する
•
そうなるケースでは再帰条件を
SFINAE にする
•
(static if ほしいです)
◆配列操作
(index_tuple idiom)•
また
reverse アルゴリズムを実装してみる
#include <sprout/array.hpp>using namespace sprout;
template<class T, size_t N>constexpr array<T, N> reverse(array<T, N> const& arr);
constexpr array
適⽤結果の配列を返す
◆配列操作
(index_tuple idiom)•
また
reverse アルゴリズムを実装してみる
#include <sprout/index_tuple.hpp>
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());}
for index_tuple, index_range
◆配列操作
(index_tuple idiom)•
また
reverse アルゴリズムを実装してみる
#include <sprout/index_tuple.hpp>
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_range<0, N>::type は index_tuple<0..N-1> を返す
0..N-1 に推論され る
Pack expansion expression によってarr[N-1]..arr[0] に展開される
◆配列操作
(index_tuple idiom)•
index_tuple イディオムは、インデックスアクセス可能
なデータ構造⼀般に適⽤できる–
配列
–
タプル–
ランダムアクセスイテレータ
–
型リスト
•
constexpr に限らず、通常の関数や
TMP にも同じよう に応⽤できる
•
ただしインデックスアクセス不可なデータ構造には適⽤ できない
–
⾮ランダムアクセスなイテレータなど
◆クラスを
constexpr 化する•
コンストラクタで処理を委譲
•
状態を書き換えるメンバ関数の constexpr 化
◆コンストラクタで処理を委譲•
Delegating constructor
#include <sscrisk/cel/algorithm.hpp>using namespace sscrisk::cel;
template<class T>struct minmax_t {
template<class Iter>constexpr minmax_t(Iter first, Iter last);
private:T min_, max_;
};
constexpr minmax_element
あるイテレータ範囲の
min と
max を保持するクラス
constexpr コンストラクタでは初期化⼦リストにしか
処理を書けない
◆コンストラクタで処理を委譲•
Delegating constructor
template<class T>struct minmax_t {
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)
{ }T min_, max_;
};
まず
minmax
を求めてから別のコンストラクタに処理を丸投げ
minmax を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)
{ }T min_, max_;
};
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_;
};
実装⽤クラスに処理を丸投げ
実装⽤クラスで
minmax をmin と
max に振り分け
private 継承
実装⽤クラスのメンバを using
◆状態を書き換えるメンバ関数•
⾮
constexpr クラス
•
constexprクラス
struct sha1 {void process_byte(unsigned char byte);template<class Iter>void process_block(Iter first, Iter last);
};
struct sha1 {constexpr sha1 process_byte(unsigned char byte) const;template<class Iter>constexpr sha1 process_block(Iter first, Iter last) const;
};
状態を書き換える↓
「次の状態」を返すように
◆状態を書き換えるメンバ関数•
⾮
constexpr クラス
•
constexprクラス
struct random_generator {unsigned int operator()();
};
struct random_generator {constexpr pair<unsigned int, random_generator> operator()() const;
};
状態を書き換え、かつ処理結果を返す
↓処理結果と「次の状態」の
タプルを返す
◆constexpr の3⼤コスト•
オブジェクトコピーのコスト
•
再帰とテンプレートインスタンス化のコ スト
•
コーディング上のコスト
(⼈間のコスト)
◆オブジェクトコピーのコスト•
配列の2つの要素の
swap を考えてみる
–
⾮
constexpr の場合
(元の配列を書換え)
–
constexpr の場合
(別の配列をつくる)
•
単なる要素の交換に線形時間
O(n) を要する
⾼々1回のコピーと2回の代⼊
(またはムーヴ)
常に全要素のコピー
◆オブジェクトコピーのコスト•
状態を書き換えるメンバ関数の呼出しを考えてみる
•
状態を書き換えるメンバ関数を
constexpr にすると、 (オブジェクトコピー×呼出し回数)の計算量が余計に かかる
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());
計算量=(計算毎+コピー毎)×要素数
計算量=計算毎×要素数+コピー
◆オブジェクトコピーのコスト•
[回避するには]
•
状態を変更する呼出しは可能な限り少なくする–
(1つの関数で済ませる)
•
Variadic function や
index_tuple イディオムを活⽤し てオブジェクトコピーが⾏われない処理を書く
◆再帰とテンプレート実体化のコスト•
再帰を途中で打ち切るアルゴリズムを考えてみる
template<class T, size_t N, class... Args>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 array<T, N>{{ args... }};
}template<class T, size_t N, class... Args>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)]);}
引数 val と等しい要素があったらそこまでを新しい配列にコピーする
SFINAE の番兵
◆再帰とテンプレート実体化のコスト•
再帰を途中で打ち切るアルゴリズムを考えてみる
•
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);
再帰は 5 まで
再帰は 5 まで
要素数が
20
◆再帰とテンプレート実体化のコスト•
再帰を途中で打ち切るアルゴリズムを考えてみる
•
実際に評価される再帰が何回だったとしても(例え0回 でも)SFINAE で打ち切られるまで全てのテンプレート がインスタンス化されるため
–
(copied_1
は
10,copied_2
は
20)
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);
◆再帰とテンプレート実体化のコスト•
[回避するには]
•
インデックスアクセス可能かつ線形探索ならば常に 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>
t lt T E i t if i t(E i t& T i l T l td f l 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 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>
t lt T E i t if i t(E i t& T i l T l td f l t )
これは宣⾔だけ書き出して⼀部省略しているので実際のコードはもっと酷い引数コピペの嵐
元々はたった2 個の関数だった
18
個の実装関数
◆コーディング上のコスト•
名前付けが酷い
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>
t lt T E i t if i t(E i t& T i l T l td f l t )
generate_uniform_int_true_2_1 とかgenerate_uniform_int_true_2_2 とかgenerate_uniform_int_true_2_3 ...
そもそも意味論でなく単に処理フローで分割しているだけなので、
各関数に意味のある名前を付けるのが難しい
constexpr ラムダ式があれば救われる(かもしれない)
◆コーディング上のコスト•
[回避するには]
•
次期
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;std::cout << str << "¥n"; // "Hello world!"
constexpr auto hell = str.substr(0, 4);std::cout << hell << "¥n"; // "Hell"
}
型には要素数(最⼤⽂字数)が含まれる
要素数が増える場合:string<N> + string<M>
→ string<N + M>
要素数が減る場合:string<N>::substr
→ string<N>
◆constexpr ⽂字列• Sprout.String 実装
• ⽂字列⻑が変わる操作:– 静的に決定できる場合は型レベルで解決– そうでなければ
len を弄って変更
– あるいはユーザ側に最⼤⻑を指定させる
namespace sprout {template<class T, size_t N, Traits = char_traits<T> >class basic_string {
T elems [N + 1];size_t len;
};}
ヌル終端を含めた固定⻑バッファ
⽂字列⻑ (len <= N)
◆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>
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);}
hex8f
: 8bit16進数にマッチ
◆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);}
repeat
: lim<N>回の繰り返しarray<Attr, N * M>
◆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);}
>>
: パーサの連結array<Attr, N + M>
◆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);}
|
: パーサのORvariant<Attr1, Attr2>
◆constexpr 構⽂解析• Sprout.Weed
–
Boost.Spirit.Qi のような
Expression Template ベースの EDSL 構⽂解析ライブラリ
•
Expression Template 技法では、処理が細かい単位に 分割されるので、constexpr 化しやすい
•
constexpr の導⼊によって、コンパイル時⽂字列処理が 相当⼿軽に出来るようになった
•
おそらく今後
constexpr 正規表現などのライブラリも 出てくると思われる
◆constexpr レイトレーシング• Sprout.Darkroom
◆constexpr レイトレーシング• Sprout.Darkroom
#include <sprout/darkroom.hpp>#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;
512×512 pixelで作成
◆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(
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))
);
オブジェクトの定義(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) {
std::cout<< unsigned(colors::r(pixel)) << ' '<< unsigned(colors::g(pixel)) << ' '<< unsigned(colors::b(pixel)) << std::endl;
}}
}
標準出⼒へppm 形式で出⼒
◆constexpr レイトレーシング• Sprout.Darkroom
–
metatrace という
TMP ライブラリを元にした
constexpr レイ トレーサーライブラリ
•
レイトレーシングの基本的なアルゴリズムはとてもシン プル
•
視点から各ピクセルを通る光線を⾶ばして、ベクトルが オブジェクトと衝突する部分の拡散光と反射光の成分を 得るだけ
◆次期
C++1x に期待すること• いくつかのポインタ演算を定数式扱いに
• union の任意のメンバでの初期化を定数式扱いに
p1 < p2;
p1 - p2;
static_cast<int const*>( static_cast<void const*>(p) )
ポインタの⼤⼩⽐較はできない
ポインタ同⼠の減算はできない
void* から他の型への読み替えはできない
union U {X x; Y y;constexpr U() : y() { }
}; 先頭メンバ以外での初期化はできない
◆次期
C++1x に期待すること• ラムダ式を定数式扱いに
• new/delete を定数式に出来るように
• 標準ライブラリを更に
constexpr 化
template<class F>constexpr auto call(F f) -> decltype(f()) { return f(); }
call( []{ return 0; } );
constexpr 関数にラムダ式を渡せない
◆constexpr 化できそうなもの• 正規表現
– Regex / Expressive• 汎⽤
Expression Template
– Proto• シリアライズ
– Serialization• 画像処理
– GIL• 数学関数
– Math.SpecialFunction• etc...
◆まとめ•
constexpr で表現可能な応⽤範囲はとて
も広い•
コーディング上の制約と落とし⽳は多い
ので気をつける•
数値計算が得意分野
•
constexpr で遊ぼう!
ご静聴ありがとう ございました