C++ で Boost.MPL を始めるなら知っておきたい10のこと

元ネタ:HerokuでWebアプリ開発を始めるなら知っておきたい10のこと


いいぜ、誰も書かないっていうなら、まず、俺が(ry。


と、そんな感じで、元ネタとの関係性は全くありませんが、『広めるためにはまず自分から……』って事で。
多分、誰も後に続かないだろうけども。
まぁ内容が適当なのでいつも通りです。
間違っててもいじめないでくだしあ><


ようこそ、コンパイルの世界へ。

1.BOOST_MPL_ASSERT

BOOST_STATIC_ASSERT(static_assert) の、Boost.MPL 版です。
内部で、"::value" を展開してくれます。

#include <boost/type_traits/is_same.hpp>
#include <boost/mpl/assert.hpp>

BOOST_MPL_ASSERT(( boost::is_same<int, int*> ));

[コンパイル出力]

no matching function for call to 'assertion_failed(mpl_::failed************ boost::is_same::************)'

他にも BOOST_MPL_ASSERT_NOT や、BOOST_MPL_ASSERT_MSG があります。
※マクロが 二重括弧(())になっていることに注意

2.Numeric

Boost.MPL では、値を型として扱います。

#include <boost/mpl/int.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/equal_to.hpp>

namespace mpl = boost::mpl;

typedef mpl::int_<3> _3;
typedef mpl::int_<5> _5;

BOOST_MPL_ASSERT(( mpl::equal_to<
    mpl::plus<_3, _5>, mpl::int_<8> 
> ));

BOOST_MPL_ASSERT    (( mpl::not_<mpl::bool_<false> > ));
BOOST_MPL_ASSERT_NOT(( mpl::or_<mpl::false_, mpl::false_> ));

演算用のメタ関数があるので、四則演算も行う事が出来ます。
Boost.MPL では、"static const int value" ではなく、mpl::int_ が値として使用されます。

3.Sequence

Boost.MPL では、いくつかの Sequence が用意されています。

#include <boost/mpl/vector.hpp>
#include <boost/mpl/list.hpp>
#include <boost/mpl/equal.hpp>

namespace mpl = boost::mpl;

typedef mpl::vector<int, char, float> seq1;
typedef mpl::vector<int, char, float> seq2;

// mpl::equal は、Sequence の比較を行う
BOOST_MPL_ASSERT(( mpl::equal< seq1, seq2 > ));

この様な形で定義、使用する事が出来ます。

4.Algorithm

Sequence を扱う為に、STL の様な Algorithm が用意されています。

#include <boost/mpl/vector.hpp>
#include <boost/mpl/distance.hpp>
#include <boost/mpl/count.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/int.hpp>

namespace mpl = boost::mpl;

typedef mpl::vector<int, int, char, float> seq;

// Sequence の begin と end を使用する
typedef mpl::distance<
    mpl::begin<seq>::type, mpl::end<seq>::type
> distance;
BOOST_MPL_ASSERT((
    mpl::equal_to<distance, mpl::int_<4> >
));

// Sequence をそのまま使用する
typedef mpl::count<seq, int> num;
BOOST_MPL_ASSERT((
    mpl::equal_to<num, mpl::int_<2> >
));

iterator を渡す必要があったり、Sequence を直接渡したりする細かい使用は、リファレンスを見てください。

5.Placeholders

Placeholders を使用して部分評価を行うことが出来ます。

#include <boost/type_traits/is_integral.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/count_if.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/vector_c.hpp>

typedef mpl::vector<int, int, char, float, short> seq;

typedef mpl::count_if<
    seq,
    boost::is_integral<mpl::_1>
> integral_num;

BOOST_MPL_ASSERT((
    mpl::equal_to<integral_num, mpl::int_<4> >
));

// 定数 の Sequence で比較する場合は、
// mpl::equal_to<mpl::_1, mpl::_2> を使用しなければいけない
BOOST_MPL_ASSERT(( mpl::equal<
    mpl::vector_c<int, 1, 2, 3>, mpl::range_c<int, 1, 4>,
    mpl::equal_to<mpl::_1, mpl::_2>
> ));

count_if などに条件を指定する場合に、_1 を仮の型として渡すことで部分評価が行われます。
Boost.MPL では、template class で受け取るのではなく、Placeholders を使用して部分評価を行います。

6.Apply

Apply を 使用することで Placeholders で定義された型の評価を行います。

#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/modulus.hpp>
#include <boost/mpl/equal_to.hpp>

namespace mpl = boost::mpl;

// _1  % 2 == 0
typedef mpl::equal_to<
    mpl::modulus<mpl::_1, mpl::int_<2> >,
    mpl::int_<0>
> is_even;

BOOST_MPL_ASSERT((
    mpl::apply<is_even, mpl::int_<4> >::type
));

BOOST_MPL_ASSERT_NOT((
    mpl::apply<is_even, mpl::int_<7> >::type
));

7.Lambda

Lambda を使用することで、Apply を介さずに Placeholders を評価することが出来ます。

#include <boost/mpl/lambda.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/modulus.hpp>
#include <boost/mpl/equal_to.hpp>

namespace mpl = boost::mpl;

typedef mpl::lambda<
    mpl::equal_to<
        mpl::modulus<mpl::_1, mpl::int_<2> >,
        mpl::int_<0>
    >
>::type is_even_func;

// ::apply を使用して、評価を行う
BOOST_MPL_ASSERT((
    is_even_func::apply<mpl::int_<4> >
));

BOOST_MPL_ASSERT_NOT((
    is_even_func::apply<mpl::int_<7> >
));

apply が内部関数として定義されているので、それを使用して評価を行います。

8.mpl::print

コンパイル時に型名を"警告"として出力してくれます。

#include <boost/mpl/int.hpp>
#include <boost/mpl/modulus.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/lambda.hpp>
#include <boost/mpl/print.hpp>

namespace mpl = boost::mpl;

typedef mpl::lambda<
    mpl::equal_to<
        mpl::modulus<mpl::_1, mpl::int_<2> >,
        mpl::int_<0>
    >
>::type is_even_func;

// print<>::type として定義することで出力が行われる
typedef mpl::print<
    is_even_func::apply<mpl::int_<2> >::type
>::type result;

[コンパイル時出力]

コンパイルされたクラスの テンプレート のインスタンス化 'boost::mpl::print' の参照を確認してください
with
[
T=boost::mpl::bool_
]
print::type を定義することで、type がコンパイル時に出力されます。 void を渡せなかったり、アンドキュメントだったり、typedef した型がうまく表示されなかったりとちょっと注意がいります。

9.mpl::string

コンパイル時に文字列を扱う事ができます。
#include <boost/mpl/string.hpp>
#include <iostream>

typedef mpl::string<'hell', 'o wo', 'rld'> hello_world;

int main(){
    std::cout << mpl::c_str<hello_world>::value << std::endl;
}
[出力]
hello world
実行時の文字列のポインタは、mpl::c_str<>::value を使用して、取得することが出来ます。

10.fizz_buzz

まとめとして、コンパイル時に fizz_buzz を行います。
#include <boost/mpl/int.hpp>
#include <boost/mpl/modulus.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/string.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/range_c.hpp>
#include <iostream>

namespace mpl = boost::mpl;

typedef mpl::equal_to<
    mpl::modulus<mpl::_1, mpl::int_<3> >,
    mpl::int_<0>
> is_fizz;

typedef mpl::equal_to<
    mpl::modulus<mpl::_1, mpl::int_<5> >,
    mpl::int_<0>
> is_buzz;

typedef mpl::and_<is_fizz, is_buzz> is_fizz_buzz;

typedef mpl::lambda<
    mpl::if_<
        is_fizz_buzz,
        mpl::c_str<mpl::string<'fizz', 'buzz'> >,
        mpl::if_<
            is_fizz,
            mpl::c_str<mpl::string<'fizz'> >,
            mpl::if_<
                is_buzz,
                mpl::c_str<mpl::string<'buzz'> >,
                mpl::_1
            >
        >
    >
>::type fizz_buzz;

struct disp{
    template<typename T>
    void operator ()(T) const{
        std::cout << fizz_buzz::apply<T>::type::value << std::endl;
    }
};

int main(){
    mpl::for_each<mpl::range_c<int, 1, 20> >(disp());
}
こんな感じで、コンパイル時に fizzbuzz を処理することが出来ます。 if_ とか、for_each とか、range_c とか、説明してないものも使用していますが、そこはがんばって自分で調べてください! そんな感じで、適当に書いてみました。 内容はあってないようなものですが、まぁ詳しくはリファレンスを参照してください。 とりあえず、 『俺はちゃんとを書いたんだから次はお前がやれよ!!』 的な感じで、早く誰か C++ とか、Boost とか、vim とかを書いてくだしあ。 他力本願ばんざーいい。 [boost] ver 1.46.1 [参照] http://www.boost.org/doc/libs/1_46_1/libs/mpl/doc/refmanual/refmanual_toc.html