55
Boost.MPL をつかってみよう 2013/06/22 Sat. Boost 勉強会12 #大阪

Try to use boost.mpl

  • Upload
    suikaba

  • View
    1.426

  • Download
    3

Embed Size (px)

Citation preview

Page 1: Try to use boost.mpl

Boost.MPL をつかってみよう

2013/06/22 Sat.

Boost 勉強会12 #大阪

Page 2: Try to use boost.mpl

目次

● 自己紹介

● はじめに

● Boost.MPL

● おまけ

● まとめ

Page 3: Try to use boost.mpl

自己紹介

Page 4: Try to use boost.mpl

自己紹介● すいかばー (@suibaka)

● 17歳(高校2年生)

● 2年前にC++と運命の出会い

● 刺激(×まさかり)を求めて

Boost勉強会へ初参加

● よく大学生に間違われます

(実際おっさん)

● スイカバーおいしい!!

Page 5: Try to use boost.mpl

Introductionはじめに

Page 6: Try to use boost.mpl

今!!constexprが

Page 7: Try to use boost.mpl

ですが

constexprにもできないことがあります。

それは型を扱うことです。

これからはTMPとconstexprを共存させ、メタプログラミングをやっていくことが大切です。

今日はTMPに焦点を当て、その時にちょっと便利なBoost.MPLの話をします

Page 8: Try to use boost.mpl

What's Boost.MPL?

名前の通り、TMPを支援してくれる便利なライブラリ。Boostの内部実装や、色んなところで使われています。

例えば…● mpllibs (https://github.com/sabel83/mpllibs)

 コンパイル時に構文解析とか出来るライブラリ

Page 9: Try to use boost.mpl

諸注意

以降、例として出てくるコードは以下のものが省略されています。

namespace mpl = boost::mpl;using namespace mpl::placeholders;その他必要な #include

Page 10: Try to use boost.mpl

Metafunction普通の関数が引数をとって戻り値を返すのに対し、メタ関数はテンプレートパラメータを受け取ってクラス内のtypedefを結果とする。

template <typename T>struct add_ptr { using type = T*; };

// 引数 int で add_ptr を呼び出すusing res = add_ptr<int>::type;// res == int*BOOST_STATIC_ASSERT(( std::is_same<res, int*>::value ));

Page 11: Try to use boost.mpl

Metafunction forwardingメンバにtypeがあるmetafunctionをpublic継承するテクニックのこと。

template <typename T>struct is_int : std::false_type{};template <>struct is_int<int> : std::true_type{};

BOOST_STATIC_ASSERT((!is_int<char>::value));BOOST_STATIC_ASSERT((is_int<int>::value));

Page 12: Try to use boost.mpl

Boost.MPL

Page 13: Try to use boost.mpl

Integral/Boolean Wrapper

using int_zero = mpl::int_<0>;using integral_one = mpl::integral_c<long, 1>;using true_ = mpl::bool_<true>; // MPLにこのまま定義されてる

BOOST_STATIC_ASSERT(( int_zero::value == long_zero::value ));BOOST_STATIC_ASSERT(( true_::value ));

Page 14: Try to use boost.mpl

Comparison Operators左右でそれぞれ対応した意味を持つ。

mpl::equal_to<A, B>::value A::value == B::value

mpl::not_equal_to<A, B>::value A::value != B::value

mpl::greater<A, B>::value A::value > B::value

mpl::greater_equal<A, B>::value A::value >= B::value

mpl::less<A, B>::value A::value < B::value

mpl::less_equal<A, B>::value A::value <= B::value

equal_to<A,B>::type::value ともかけるが、これはequal_to<A,B>::value と同じ意味。(equal_toの::typeは自身の型を返すため)。

Page 15: Try to use boost.mpl

If statement

template <typename T>struct param_type : mpl::eval_if< mpl::or_<std::is_scalar<T>, std::is_reference<T>> , mpl::identity<T> , boost::add_reference<T> >{};

BOOST_MPL_ASSERT((std::is_same<param_type<int>::type, int>));BOOST_MPL_ASSERT(( std::is_same<param_type<std::string>::type, std::string&>));

Page 16: Try to use boost.mpl

template <typename T>using is_pointer_or_reference = mpl::or_<std::is_pointer<T>, std::is_reference<T>>;

BOOST_STATIC_ASSERT(( is_pointer_or_reference<int*>::value ));BOOST_STATIC_ASSERT(( !is_pointer_or_reference<char>::value ));

mpl::not_<metafunction>mpl::or_<metafunction1, metafunction2, ..., metafunctionN>mpl::and_<metafunction1, metafunction2, ..., metafunctionN>

引数に::valueを持つmetafunctionを渡す。::valueでその結果が得られる。

Page 17: Try to use boost.mpl

Sequences

mpl::vector<char, int, double>mpl::list<short, long, float>mpl::map< // mapはmpl::pairを要素に持つ mpl::pair<int, unsigned int> , mpl::pair<double, unsigned double>>

list, vector, deque, map, setMPLのSequenceは型が要素となる。

Page 18: Try to use boost.mpl

Integral Sequence Wrappers

mpl::list_c<T, 1, 3, ..., 9>

数値型(mpl::integral_c)を格納するために特化したSequence。list_c, vector_c, set_cが存在する。

mpl::list< mpl::integral_c<T, 1> , mpl::integral_c<T, 3> , ... , mpl::integral_c<T, 9>>

上記のコードは以下と同じ意味

Page 19: Try to use boost.mpl

range_c

mpl::vector< mpl::integral_c<T, N> , mpl::integral_c<T, N+1> , ... , mpl::integral_c<T, M-2> , mpl::integral_c<T, M-1> // NからM-1まで>

連続した数値型のリスト。じつは遅延評価。range_c<T, N, M> で下記のようなイメージ※あくまでもイメージです。ホントは違います

Page 20: Try to use boost.mpl

Iterators

using v = mpl::vector<char, int, double>;using int_pos = mpl::find<v, int>::type; // int_posがiteratorusing result = mpl::deref<int_pos>::type; // int_posの参照剥がしBOOST_STATIC_ASSERT(( std::is_same<result, int>::value ));

std::vector<int> v = { 1, 2, 3, 4 };auto three_pos = std::find( std::begin( v ) , std::end( v ) , 3 );int result = *three_pos;assert( result == 3 );

上記のコードは以下のコードととても似ている。

Page 21: Try to use boost.mpl

category

Sequenceには種類があり、できることやできないことが決まっている。● Forward Sequence● Bidirectional Sequence● Random Access Sequence● Extensible Sequence● Front Extensible Sequence● Back Extensible Sequence● Associative Sequence● Associative Extensible Sequence

Iteratorも同様。● Forward Iterator● Bidirectional Iterator● Random Access Iterator

もっと詳しく知りたい人はBoost.MPLのリファレンスを見ましょう。

Page 22: Try to use boost.mpl

Sequences -concepts-

using v = mpl::push_back<mpl::vector<int>, char>::type;using m = mpl::insert<mpl::map<>, mpl::pair<char, int>>::type;

BOOST_MPL_ASSERT((mpl::equal<v, mpl::vector<int, char>));BOOST_MPL_ASSERT((mpl::equal<m, mpl::map<mpl::pair<char, int>>));

一応簡単に。STLのコンテナにそれぞれ特徴があるのと同じ。※MPLの場合はメンバ関数ではない。

→とりあえず vector(テンプレ

Page 23: Try to use boost.mpl

Algorithms結果は型として返される。

using v = mpl::vector<int, long>;using res = mpl::transform<v, boost::add_pointer<_>>::type;

BOOST_MPL_ASSERT((mpl::equal<res, mpl::vector<int*, long*>>));

using types = mpl::vector<short, char, double, long>;using max_size_it = mpl::iter_fold< types , mpl::begin<types>::type , mpl::if_< mpl::less<mpl::sizeof_<mpl::deref<_1>> , mpl::sizeof_<mpl::deref<_2>>> , _2, _1 >>;BOOST_MPL_ASSERT((std::is_same<mpl::deref<max_size_it>::type, double>));

Page 24: Try to use boost.mpl

Algorithms

mpl::find<seq, T> 型の検索。なければmpl::end<seq>mpl::find_if<seq, pred> 述語を使った検索。後はfindと同じmpl::contains<seq, T> seqにTが含まれるかどうかmpl::count<seq, T> Tがいくつ含まれているかmpl::count_if<seq, pred> predを満たすものが含まれている数mpl::equal<seq1, seq2> seq1とseq2の要素が同じかmpl::lower_bound<seq, T, pred> 最初にpredが満たされたところmpl::upper_bound<seq, T, pred> 最後にpredが満たされたところmpl::max_element<seq> 要素で最大のものを得るmpl::min_element<seq> 要素中で最小のものを得る

Metafunction ::type で得られる結果

Page 25: Try to use boost.mpl

Algorithms

mpl::copy<seq> 同じ要素を持った型mpl::copy_if<seq, pred> predを満たすものを持った型mpl::remove<seq, T> seqからTを削除した型mpl::remove_if<seq, pred> predを満たす型を削除した型mpl::replace<seq, old, new> seqのoldの要素をnewに置換した型mpl::replace_if<seq, pred, new> predを満たす型をnewに置換した型mpl::reverse<seq> seqの要素を逆順にした型mpl::transform<seq, op> seqの各要素にopを適用した型mpl::transform<seq1, seq2, op> opが2項であること以外は上と同じmpl::unique<seq> 重複要素を削除した型

Metafunction ::type で得られる結果

Page 26: Try to use boost.mpl

inserter

template <typename State, typename Operation>struct inserter{ using state = State; using operation = Operation;};

state:操作中のSequenceの状態operation:stateから新しいstateを作るのに使われる

Page 27: Try to use boost.mpl

inserter

using v = mpl::vector<char, int>;using res = mpl::copy< mpl::list<long, double> , mpl::back_inserter<v> // inserter<v, push_back<_>> >::type;

BOOST_STATIC_ASSERT(( mpl::equal< res, mpl::vector<char, int, long, double> >::value));

inserterの使い方。vectorにlistの要素を追加する。

Page 28: Try to use boost.mpl

inserter

using v = mpl::vector<char, int>;using res = mpl::copy< mpl::list<long, double> , mpl::back_inserter<v> // inserter<v, push_back<_>> >::type;

BOOST_STATIC_ASSERT(( mpl::equal< res, mpl::vector<char, int, long, double> >::value));

inserterの使い方。vectorにlistの要素を追加する。

シーケンスが違ってもOK

Page 29: Try to use boost.mpl

Sequence Views

Sequenceを操作するAlgorithm(e.g., mpl::transform)をより強力にしたもの。

何が強力?例えば、transform_view<S,Pred>は必要になるまでSequenceの各要素にPredを適用しない。いわゆる遅延評価。

Page 30: Try to use boost.mpl

transform_viewusing seq1 = mpl::vector<int, char, volatile float>;using seq2 = mpl::list<double, short, long>;template <typename Seq>using contains_float = mpl::contains< mpl::transform_view< Seq , boost::remove_cv<_> > , float>;

BOOST_MPL_ASSERT(( contains_float<seq1> ));BOOST_MPL_ASSERT_NOT(( contains_float<seq2> ));

Page 31: Try to use boost.mpl

pair_view// 2つのSequenceの各要素をmpl::pairでくっつけるusing l = mpl::list<char, int, short>;using v = mpl::vector<long, float, double>;using view = mpl::pair_view<l, v>;using first = mpl::begin<view>::type;using it = mpl::advance<first, mpl::int_<2>>::type;

BOOST_MPL_ASSERT(( std::is_same< mpl::deref<it>::type , mpl::pair<short, double> >));

Page 32: Try to use boost.mpl

single_view/joint_view// 任意の型Tを一要素のシーケンスとして表現using view = mpl::single_view<int>;

BOOST_MPL_ASSERT(( std::is_same<mpl::begin<view>::type, int> ));

// 2つのSequenceを連結using v = mpl::vector<char, int>;using l = mpl::list<float, double>;using view = mpl::joint_view<v, l>;

BOOST_MPL_ASSERT(( mpl::equal< mpl::vector<char, int, float, double>, view> ));

Page 33: Try to use boost.mpl

filter_view

// ポインタだけ選りすぐるusing seq = mpl::vector<char&, long*, double, int*&>;using res = mpl::transform_view< mpl::filter_view<seq, boost::is_pointer<_1>> , boost::remove_pointer<_1>>;

using excepted = mpl::vector<long>;BOOST_MPL_ASSERT(( mpl::equal<excepted, res> ));

Page 34: Try to use boost.mpl

自分でSequenceを書く

MPLのシーケンスと同じように設計することで、MPLの有用なAlgorithmをそのまま利用できます。

具体的には…● 作りたいシーケンスのタグを作り、そのシーケンスにtagと

いう名前でtypdefする● アルゴリズムの実装メタファンクション(e.g.,

mpl::clear_impl, mpl::push_back_impl ...)をそのタグで特殊化し、実装する

● あとは普段通りアルゴリズム呼び出すだけ!

Page 35: Try to use boost.mpl

おまけTMPで使われるルール&テクニック

Page 36: Try to use boost.mpl

SFINAE

template <typename T>struct has_f{ template <typename U> static auto check(U x) -> decltype(x.f(), std::true_type()); static std::false_type check(...);

public: static const bool value = decltype(check(std::declval<T>()))::value;};

// 続く...

テンプレートの実体化に失敗してもエラーにしないルール。実体化→オーバーロード解決の順に行われるので、失敗したものをオーバーロードの候補から取り除ける。

Page 37: Try to use boost.mpl

SFINAEtemplate <typename T>auto g(T x) -> decltype(x.f()){ std::cout << "has f" << std::endl; return x.f();};template <typename T, typename = typename std::enable_if<!has_f<T>::value>::type>void g(T x){ std::cout << "does not has f" << std::endl;}

struct A {};struct B { int f() { return 1; } };int main(){ A a; B b; g(a), std::cout << g(b) << std::endl;}

does not have fhas f1

Page 38: Try to use boost.mpl

要件を満たさない関数の削除

void f(int) = delete; // #1template <typename T>void f(T t) {} // #2template <typename T> requires character<T>() // char, wchar_t, ...void f(T t) {} = delete; // #3 C++11では動きません

int main(){ f(0.0); // OK f('a'); // Error! f(0); // Error!}

ある条件を満たす場合以外、その関数を削除してコンパイルエラーにすることができます。

Page 39: Try to use boost.mpl

Variable Templates(C++14)

template <Num N>constexpr N min = std::numeric_limits<T>::min();

std::cout << min<int> << std::endl;std::cout << min<unsigned> << std::endl;

変数にもtemplateを指定できるようになる

Page 40: Try to use boost.mpl

Concept

template <typename T>concept bool equality_comparable(){ return requires (T a, T b) { {a == b} -> bool; {a != b} -> bool; };}

template <typename T> requires equality_comparable<T>()void f(T a, T b) { ... };

いずれ入るであろうconcept。

Page 41: Try to use boost.mpl

Concept

concept ってどういう意味があるの?

● constexprである● 特殊化されない● 定義を必ず持つ● type specifier(型指定子)として使用される(可能性があ

Page 42: Try to use boost.mpl

Concept

Conceptが入るとtemplateのオーバーロード解決が少し変わる

テンプレート引数の推論

constraints(制約)の実体化とそれが有効かどうかの確認

宣言を実体化する

残った候補の中から最もマッチしたもの(特殊化、制約)を選択

Page 43: Try to use boost.mpl

Concept

[x]<equality_comparable T>(T y) { return x == y; }// 又は[x](equality_comparable y) { return x == y; }

ラムダにも使える?らしい

template <input_iterator I>void advance(I& it);template <bidirectional_iterator I>void advance(I& it);template <random_access_iterator I>void advance(I& it);

オーバーロードとかもできるらしい

Page 44: Try to use boost.mpl

実際の使用例

Page 45: Try to use boost.mpl

実際の使われ方はどんな感じ?

冒頭ででてきたmpllibsにsafe_printfというものがあるので、試しにその一部の実装をのぞいてみましょう!

int main(){ using mpllibs::safe_printf::printf;

printf<MPLLIBS_STRING("%s, %d")>("hoge", 11); printf<MPLLIBS_STRING("%s, %d")>(11, "hoge"); // error}

Page 46: Try to use boost.mpl

基本となるstring。下記のようにtagを作っておくと、Boost.MPLと連携しやすくなります。

struct string_tag // タグディスパッチするため{ using type = string_tag;};

template <char... Cs>struct string{ using type = string; using tag = string_tag;};

Page 47: Try to use boost.mpl

mpllibs safe_printftemplate <class S, char C>struct push_front_ch;template <char... Cs, char C>struct push_front_ch<string<Cs...>, C>

: string<C, Cs...>{};

namespace boost { namespace mpl {template <>struct clear_impl<string_tag> // string_tagで特殊化すると、{ // mpl::clear<string<...>>でこれが呼ばれる using type = clear_impl;

template <class S> struct apply : MPL::string<> {};};}}

Page 48: Try to use boost.mpl

次に"hoge"という文字列から'h', 'o', 'g', 'e'を得ていくために、一文字ずつ抜き出してくる関数があります。

#define MPL_NO_CHAR EOF

template <int L, class T>constexpr int string_at(const T (&s)[L], int n){ // 要素外なら MPL_NO_CHAR を返す return n >= L-1 ? MPL_NO_CHAR : s[n];}

Page 49: Try to use boost.mpl

mpllibs safe_printf

template <class S>struct remove_no_chars : S{};template <char... Cs>struct remove_no_chars<string<MPL_NO_CHAR, Cs...>> : mpl::clear<string<MPL_NO_CHAR, Cs...>>{};template <char C, char... Cs>struct remove_no_chars<string<C, Cs...>> : push_front_ch<typename remove_no_chars<string<Cs...>>::type, C>{};

Page 50: Try to use boost.mpl

mpllibs safe_printf

#define MPL_STRING_MAX_LENGTH 32

#define MPLLIBS_STRING_N(z, n, s) string_at((s), n)#define MPLLIBS_STRING(s) \ remove_trailing_no_chars< \ string< \ BOOST_PP_ENUM(MPLLIBS_STRING_MAX_LENGTH, MPLLIBS_STRING_N, s) \

> \ >::type

Page 51: Try to use boost.mpl

まとめ

Page 52: Try to use boost.mpl

まとめ

あくまでも…MPLはTMPを補助する立ち位置。使用が目的では無いちょっとだけ使ってみるのも全然あり。使えそうなら使ってみましょう!

使うときは、ユーザーコードにMPLのコードができるだけ現れないようにしましょう。ライブラリ内で完結させたほうがいいと思います。

MPL自体は古いけどまだ使えます

Page 53: Try to use boost.mpl

本当は…

コンパイル時のパフォーマンスとか、どうすればもっと改善できるかとかの話もしたかった。→まとめきれませんでしたゴメンナサイ。

Page 54: Try to use boost.mpl

参考文献

● Boost.MPL Referencehttp://www.boost.org/doc/libs/1_53_0/libs/mpl/doc/refmanual.html

● N3580http://isocpp.org/blog/2013/03/new-paper-n3580-concepts-lite-andrew-sutton-bjarne-stroustrup-gabriel-dos-r

Page 55: Try to use boost.mpl

最後に

Let's enjoy Template Metaprogramming World!