• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Try to use boost.mpl
 

Try to use boost.mpl

on

  • 1,695 views

 

Statistics

Views

Total Views
1,695
Views on SlideShare
1,463
Embed Views
232

Actions

Likes
5
Downloads
6
Comments
0

1 Embed 232

https://twitter.com 232

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Try to use boost.mpl Try to use boost.mpl Presentation Transcript

    • Boost.MPL をつかってみよう2013/06/22 Sat.Boost 勉強会12 #大阪
    • 目次●自己紹介●はじめに●Boost.MPL●おまけ●まとめ
    • 自己紹介
    • 自己紹介●すいかばー (@suibaka)●17歳(高校2年生)●2年前にC++と運命の出会い●刺激(×まさかり)を求めてBoost勉強会へ初参加●よく大学生に間違われます(実際おっさん)●スイカバーおいしい!!
    • Introductionはじめに
    • 今!!constexprが
    • ですがconstexprにもできないことがあります。それは型を扱うことです。これからはTMPとconstexprを共存させ、メタプログラミングをやっていくことが大切です。今日はTMPに焦点を当て、その時にちょっと便利なBoost.MPLの話をします
    • Whats Boost.MPL?名前の通り、TMPを支援してくれる便利なライブラリ。Boostの内部実装や、色んなところで使われています。例えば…●mpllibs (https://github.com/sabel83/mpllibs) コンパイル時に構文解析とか出来るライブラリ
    • 諸注意以降、例として出てくるコードは以下のものが省略されています。namespace mpl = boost::mpl;using namespace mpl::placeholders;その他必要な #include
    • 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 ));
    • 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));
    • Boost.MPL
    • Integral/Boolean Wrapperusing 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 ));
    • Comparison Operators左右でそれぞれ対応した意味を持つ。mpl::equal_to<A, B>::value A::value == B::valuempl::not_equal_to<A, B>::value A::value != B::valuempl::greater<A, B>::value A::value > B::valuempl::greater_equal<A, B>::value A::value >= B::valuempl::less<A, B>::value A::value < B::valuempl::less_equal<A, B>::value A::value <= B::valueequal_to<A,B>::type::value ともかけるが、これはequal_to<A,B>::value と同じ意味。(equal_toの::typeは自身の型を返すため)。
    • If statementtemplate <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&>));
    • 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でその結果が得られる。
    • Sequencesmpl::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は型が要素となる。
    • Integral Sequence Wrappersmpl::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>>上記のコードは以下と同じ意味
    • range_cmpl::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> で下記のようなイメージ※あくまでもイメージです。ホントは違います
    • Iteratorsusing 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 );上記のコードは以下のコードととても似ている。
    • categorySequenceには種類があり、できることやできないことが決まっている。●Forward Sequence●Bidirectional Sequence●Random Access Sequence●Extensible Sequence●Front Extensible Sequence●Back Extensible Sequence●Associative Sequence●Associative Extensible SequenceIteratorも同様。●Forward Iterator●Bidirectional Iterator●Random Access Iteratorもっと詳しく知りたい人は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(テンプレ
    • 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>));
    • Algorithmsmpl::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 で得られる結果
    • Algorithmsmpl::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 で得られる結果
    • insertertemplate <typename State, typename Operation>struct inserter{using state = State;using operation = Operation;};state:操作中のSequenceの状態operation:stateから新しいstateを作るのに使われる
    • inserterusing 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の要素を追加する。
    • inserterusing 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
    • Sequence ViewsSequenceを操作するAlgorithm(e.g., mpl::transform)をより強力にしたもの。何が強力?例えば、transform_view<S,Pred>は必要になるまでSequenceの各要素にPredを適用しない。いわゆる遅延評価。
    • 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> ));
    • 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>>));
    • 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> ));
    • 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> ));
    • 自分でSequenceを書くMPLのシーケンスと同じように設計することで、MPLの有用なAlgorithmをそのまま利用できます。具体的には…●作りたいシーケンスのタグを作り、そのシーケンスにtagという名前でtypdefする●アルゴリズムの実装メタファンクション(e.g.,mpl::clear_impl, mpl::push_back_impl ...)をそのタグで特殊化し、実装する●あとは普段通りアルゴリズム呼び出すだけ!
    • おまけTMPで使われるルール&テクニック
    • SFINAEtemplate <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;};// 続く...テンプレートの実体化に失敗してもエラーにしないルール。実体化→オーバーロード解決の順に行われるので、失敗したものをオーバーロードの候補から取り除ける。
    • 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
    • 要件を満たさない関数の削除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); // OKf(a); // Error!f(0); // Error!}ある条件を満たす場合以外、その関数を削除してコンパイルエラーにすることができます。
    • 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を指定できるようになる
    • Concepttemplate <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。
    • Conceptconcept ってどういう意味があるの?●constexprである●特殊化されない●定義を必ず持つ●type specifier(型指定子)として使用される(可能性がある
    • ConceptConceptが入るとtemplateのオーバーロード解決が少し変わるテンプレート引数の推論constraints(制約)の実体化とそれが有効かどうかの確認宣言を実体化する残った候補の中から最もマッチしたもの(特殊化、制約)を選択
    • 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);オーバーロードとかもできるらしい
    • 実際の使用例
    • 実際の使われ方はどんな感じ?冒頭ででてきた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}
    • 基本となるstring。下記のようにtagを作っておくと、Boost.MPLと連携しやすくなります。struct string_tag // タグディスパッチするため{using type = string_tag;};template <char... Cs>struct string{using type = string;using tag = string_tag;};
    • 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<> {};};}}
    • 次に"hoge"という文字列からh, o, g, eを得ていくために、一文字ずつ抜き出してくる関数があります。#define MPL_NO_CHAR EOFtemplate <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];}
    • mpllibs safe_printftemplate <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>{};
    • 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
    • まとめ
    • まとめあくまでも…MPLはTMPを補助する立ち位置。使用が目的では無いちょっとだけ使ってみるのも全然あり。使えそうなら使ってみましょう!使うときは、ユーザーコードにMPLのコードができるだけ現れないようにしましょう。ライブラリ内で完結させたほうがいいと思います。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
    • 最後にLets enjoyTemplateMetaprogrammingWorld!