THE BOOST MPL LIBRARY

Table of Contents

1. Preface
2. Sources
3. Mini-tutorial
3.1. Conventions used
3.2. Metafunctions
3.3. Compile-time if
3.4. apply_if
3.5. apply_if, part 2
4. Technical details
4.1. Physical structure
4.2. Dependencies
4.3. Portability
5. Acknowledgements
Bibliography

1. Preface

MPLライブラリは コンパイル時アルゴリズム,シーケンス,メタ関数クラスからなる C++ テンプレートメタプロムラミングフレームワークである. このライブラリの2つの主要なドキュメントは“MPL文書” と,ライブラリリファレンスドキュメントである. もしMPLについて聞いたことがないなら,文書を読むことから始め,それからリファレンスドキュメントとこのドキュメントの 情報に移った方がよいだろう.

2. Sources

-

最新のライブラリソースはBoost CVS のメイントランクから入手できる. Boost 1.30.0 distribution は2003/3/12のライブラリの 安定版を含んでいる.

3. Mini-tutorial

3.1. Conventions used

このチュートリアルで使われる例は,完全に修飾された名前付けを使っている,例えば単純にvector を使うのではなく std::vectorを使っている.修飾されていない名前は例自体で定義されたローカルの実体である. 名前空間boost::mplからの名前はmpl名前空間エイリアスをつかって指示される (例えば,boost::mpl::applyではなくmpl::apply). 次のような名前空間エイリアス定義が働いている,ということだ.

namespace mpl = boost::mpl;

ライブラリは,特別なヘッダboost/mpl/alias.hppを提供している.これは,上述のものと大体等価である. 代わりに,どの翻訳単位の中でも必要に応じて,常に自分の手で名前空間エイリアス定義を行うこともできる (もしあなたがより短い名前空間表記を使うなら).

3.2. Metafunctions

MPLでは,関数に相当するメタプログラミングは,“type”と適切に名付けられた, ネストされたtypedefメンバを含むclass templateである:


// 一目見ても,使いにくい
template< typename T >
struct identity 
{
    typedef T type;
};


// おそらくより使いやすい
template< typename T >
struct result_type 
{
    typedef typename T::result_type type;
};

メタ関数を“呼び出す”ことは,クラステンプレートを特定のテンプレートパラメータで インスタンス化し(メタ関数“arguments”),ネストされたtypeメンバにより結果にアクセスするのと 同じことである:

typedef identity<int>::type t1; // t1 == int
typedef result_type< std::unary_function<int,bool> >::type t2; // t2 == bool

3.3. Compile-time if

面白いテンプレートメタプログラミングは多くの意志決定コードをよく含む. 条件付き決定/ビヘイビアは直接,(部分的)クラステンプレート特殊化や,関数オーバーロード [Vel95a], [Ale00] で扱うことができるが,一般的に,スタンドアロンライブラリにとってコンパイル時式に基づいて2つの型から選択することを 可能にするプリミティブが必要である.boost::mplでは,そのようなプリミティブはif_と呼ばれる:

template< typename T >
struct heap_holder
{
 // ... 
 private:
    boost::scoped_ptr<T> m_object;
};

template< typename T >
struct stack_holder
{
 // ... 
 private:
    T m_object;
};

template< typename T >
struct can_be_on_stack
    : mpl::bool_c< (sizeof(T) <= sizeof(double)) >
{
};


// メンバ'T'を保持する場所を選ぶために'if_'を使う
template< typename T >
struct lightweight
   : private mpl::if_<
          can_be_on_stack<T>
        , stack_holder<T>
        , heap_holder<T>
        >::type
{
   // ...
};

if_テンプレートの最初のテンプレートパラメータは汎整数定数コンセプトのモデルであるべきだ. ライブラリはまた,より汎用的ではないが,非型boolテンプレートパラメータの形で条件を受け入れる, 時としてより便利な形式を提供している:

template< typename T >
struct lightweight
   : private mpl::if_c<
          (sizeof(T) <= sizeof(double))
        , stack_holder<T>
        , heap_holder<T>
        >::type
{
   // ...
};

3.4. apply_if

実行時のC++では,if文があれば,唯一の枝だけが実行されることが保証されている. 結果が必要でない枝を実行することは不要であり,非効率的だ.より重要なことだが, 時として要求されない枝は,無効であり,その実行はエラーを引き起こす.例えば, 次のコードはもし両方の文が評価されてしまったら,ひどく悪いことになる.

void fun(giraffe* g)
{
    if (g)
        cout << g->name();
    else
        cout << "no giraffe";
}

コンパイル時の世界では,事情は異なる.if_テンプレートに対して,どのパラメータがインスタンス化されるかは, それぞれのテンプレートパラメータの形式と,対応する言語規則により決定され ([ISO98], section 14.7.1) ,コンパイル時に与えられた式の値によるのではない. これは次のことを意味する. もしある特定のif_の構築処理を行おうとすれば, コンパイラはその“枝”となるテンプレートパラメータのひとつが不当な形式であると決定し, 例えコンパイル時式の値が,別の,有効なパラメータ型を“選択する” ように導いても,診断上の問題となる.

簡単に言えば, 生ポインタか,スマートポインタのどちらかであるTを実体化する時に, ポインタ型を“返す” pointed_typeメタ関数を書くにあたり,最初に無効な試行がある,ということである:

template< typename T >
struct pointed_type
{
    typedef typename mpl::if_<
          boost::is_pointer<T>
        , typename boost::remove_pointer<T>::type
        , typename T::element_type // #1
        >::type type;
};

typedef pointed_type< std::auto_ptr<int> >::type int_ptr; // ok
typedef pointed_type<char*>::type char_ptr; // error in line #1!

上のコードをコンパイルしようとすれば,次のようなものが得られる:

Error: name followed by "::" must be a class or namespace name
typename T::element_typeT == char*なら有効ではないからである.

pointed_typeを生ポインタに対して機能するようにするために必要なことがある: 1 2つの潜在的な結果を if_に渡す前にインスタンス化する代わりに, 結果を実体化するために使うことができるメタ関数を書く必要がある.それから, メタ関数を選ぶためにif_を使うことができるし, その結果を得る関数を使うのも,そのあとにすべきである.

boost::remove_pointerは既にメタ関数である.ポインタ型のelement_typeを返す補助的な関数を書く必要だけがある.

namespace aux {
template< typename T >
struct element_type
{
     typedef typename T::element_type type;
};
}

これで,boost::is_pointerの結果に基づいて呼び出す関数を選び, 結果の型にそれを適用することができる:

template< typename T >
struct pointed_type
{
 private:

    // メタ関数を選ぶ
    typedef typename mpl::if_<
          boost::is_pointer<T>
        , boost::remove_pointer<T>
        , aux::element_type<T>
        >::type func_; // #1

 public:

    // メタ関数を適用する
    typedef typename func_::type type;
};

上のものを実行可能にするために鍵となる知識は,#1の行でコンパイラが boost::remove_pointer<T>aux::element_type<T>テンプレートを インスタンス化しないように保証されるということである-- 例えif_に実際の引数として渡されても.

以上,述べてきた技はテンプレートメタプログラミングでは広く使われているものである. それは,関数呼び出しの一部としてfunc_::type操作を行うif_ と,極めて似たようなものを導入することで,ネストされたtypeメンバの選択を可能にする. MPLはそのようなテンプレートを提供する.apply_ifと呼ばれるものである. それを使えば,上と同じコードを次のように単純に書くことができる:

[
template< typename T >
struct pointed_type
{
    typedef typename mpl::apply_if<
          boost::is_pointer<T>
        , boost::remove_pointer<T>
        , aux::element_type<T>
        >::type type;
};

3.5. apply_if, part 2

“コードコンパイル”問題の解決に加え,ちょうど今学んだ,apply_ifの技術は, メタプログラミングの効率を改善するためにも使うことができる.

ポインタ修飾を条件付きで外す,boost::remove_pointer特性テンプレートの高水準ラッパを定義したいとしよう. それを,remove_pointer_ifと呼ぶ:

template<
      typename Condition
    , typename T
    >
struct remove_pointer_if
{
    typedef typename mpl::if_<
          Condition
        , typename boost::remove_pointer<T>::type
        , T
        >::type type;
};

上のものは機能するが,最も最適化された実装ではない.前の例と同様, boost::remove_pointer<T>はその結果が使われなくてもインスタンス化される. メタプログラミング世界では,コンパイル時間は重要なリソースである [Abr01], そしてそれは,不要なテンプレートインスタンス化によって浪費される.

ここで,if_apply_ifで差し替える必要性というものを考えてみよう. すでにapply_ifに渡すひとつのメタ関数を持っている - つまり,boost::remove_pointer<T>である, が,二つ目のものが必要である.それを,fと呼ぶ - f<T>::type == Tの様なものだ. これは,自分自身で書けるだろう,しかし,幸運なことにMPLはまさにこれを定義したテンプレートを提供してくれる - それは,identityと呼ばれる.この知識を活かして,次のようにしよう:

template<
      typename Condition
    , typename T
    >
struct remove_pointer_if
{
    typedef typename mpl::apply_if<
          Condition
        , boost::remove_pointer<T>
        , mpl::identity<T>
        >::type type;
};

4. Technical details

4.1. Physical structure

ライブラリは,公開コンポーネント(クラス/関数テンプレート) ひとつに付きひとつのヘッダという,きめ細かなヘッダ構成を提供する.ヘッダの名前はコンポーネントの名前に従う. 例えば,boost::mpl::apply<>テンプレートは,ヘッダboost/mpl/apply.hppで定義されている. この図式により,コンパイルの時間,ヘッダの依存関係に関して,何を使うべきか考える必要がなくなるし, ヘッダとコンポーネントの対応を記憶し,探すということをしなくてよくなる. 一緒に使われることが多い実体のための多くの混合のヘッダ(例えば,論理演算 - logical_or, logical_and, など)も提供されている. これにより,プログラムの中に多くの#includeディレクティブを記述するという仕事をしなくてよくなる.そして, ライブラリの能力を集約的に使うことができるようになる. 2

4.2. Dependencies

boost/config.hppヘッダに加え,MPLは2つの他のBoostライブラリにつよく依存している. Boost Preprocessor library [PRE], そして,Type Traits library [TTL] である.これらの依存関係は基本的なものであり,なくすことはできない. さらに, boost/mpl/assert_is_same.hpp ヘッダはBoost Static Assert library [SAL]に依存している. ライブラリのテストと例は他のBoostライブラリに依存しているだろう,たとえば Boost Bind [BBL]; テストや例を実際にコンパイルすることに興味がなければ, これらを持っている必要はない(多分あなたはもってるだろうけど).

4.3. Portability

下のリストは,このライブラリのテストが終わったコンパイラである:

  • Microsoft Visual C++ 6.0, SP 5
  • Microsoft Visual C++ .NET (7.0)
  • Metrowerks CodeWariror 7.2/8.1
  • Intel C++ Compiler 5.0, 6.0
  • GCC 2.95.3-5
  • GCC 3.1
  • Comeau C/C++ 4.2.45/4.3.0
  • Borland C++ 5.5.1

最近のテストコンパイル結果を載せた不完全な表は, http://www.mywikinet.com/mpl/log.html から入手できる.

5. Acknowledgements

Following is a list of people who in one or another way contributed to the library development. The list is work in progress!

David Abrahams, Emily Winch, Eric Friedman, Vesa Karvonen, Peter Dimov, Mat Marcus, Fernando Cacciola, Paul Mensonides, David B. Held, John Bandela, Arnaldur Gylfason, Hamish Mackenzie.

Copyright on this document. Copyright © 2002 Aleksey Gurtovoy, David Abrahams and Emily Winch.

Bibliography

[Abr01] David Abrahams and Carlos Pinto Coelho, Effects of Metaprogramming Style on Compilation Time, 2001

[Ale00] Andrei Alexandrescu, On Conversions between Types and Values, C/C++ Users Journal, October 2000

[BBL] Boost Bind library, http://www.boost.org/libs/bind/bind.html

[ISO98] ISO/IEC 14882:1998(E), Programming languages — C++, ISO/IEC, 1998

[PRE] Vesa Karvonen, Boost Preprocessor Metaprogramming library, http://www.boost.org/libs/preprocessor/doc/

[TTL] Boost Type Traits library, http://www.boost.org/libs/type_traits/

[Vel95a] Todd Veldhuizen, Using C++ template metaprograms, C++ Report, SIGS Publications Inc., ISSN 1040-6042, Vol. 7, No. 4, pp. 36-43, May 1995



1 Tがポインタかどうかを区別するpointed_typeを部分特殊化を使って実装するのは簡単である. if_はここでは,複雑な例を避けるために使っている.

2 The Boost Preprocessor library [PRE] は,非常に似たような物理的構成を持っている,実際,ライブラリはいくつか, 同じ名前のサブディレクトリを持っている(mpl/arithmetic <-> preprocessor/arithmetic, mpl/comparison <-> preprocessor/comparison, など).