Boost.TTI

レビューが終わったのが8月なので今更かよ!って感じですが。年内のうちに書いておかったので書きます。
一部のメタプログラマ歓喜の Type Traits Introspection ライブラリです。

[Type Traits Introspection Library]

Boost.TTI はある型が、メンバやメソッドを持っているかどうかを判断するためのライブラリです。
BOOST_MPL_HAS_XXX_TRAIT_DEF の拡張版だと思ってもらうのが分かりやすいと思います。
正式にリリース前なので本記事の内容はリリース時に仕様が変更される可能性もあるので注意して下さい。

[簡単な使い方]

BOOST_MPL_HAS_XXX_TRAIT_DEF と同様にマクロ関数を使用して、メタ関数を定義します。

#include <boost/tti/has_type.hpp>
#include <boost/tti/has_member_function.hpp>
#include <boost/tti/has_member_data.hpp>
#include <boost/mpl/assert.hpp>

namespace mpl = boost::mpl;


// has_xxx を定義する
BOOST_TTI_HAS_TYPE(value_type)          // has_type_value_type
BOOST_TTI_HAS_MEMBER_DATA(value)        // has_member_data_value
BOOST_TTI_HAS_MEMBER_FUNCTION(func)     // has_member_function_funcg


struct X{
    typedef int value_type;
    value_type value;

    void func(int, int){}
    int func(float){ return 0; }
};


// value_type の有無
BOOST_MPL_ASSERT((has_type_value_type<X>));

// value_type の型を指定してチェックも出来る
BOOST_MPL_ASSERT    ((has_type_value_type<X, int>));
BOOST_MPL_ASSERT_NOT((has_type_value_type<X, void>));


// プロパティ value の有無
// value の型を指定する
BOOST_MPL_ASSERT    ((has_member_data_value<X, X::value_type>));
BOOST_MPL_ASSERT_NOT((has_member_data_value<X, float>));


// メソッド func の有無
// 戻り値型と引数型を指定する
BOOST_MPL_ASSERT((
    has_member_function_func<
        X,
        void,                    // 戻り値型
        mpl::vector<int, int>    // 引数は mpl::vector で渡す
    >
));

// オーバーロードしていてもチェックできる
BOOST_MPL_ASSERT((
    has_member_function_func<
        X,
        int,
        mpl::vector<float>
    >
));

BOOST_MPL_ASSERT_NOT((has_member_function_func<X, void>));

int main(){}


こんな感じで定義したメタ関数に型を渡して使用します。
また、BOOST_TTI_TRAIT_{macro name} でメタ関数名を指定することも出来ます。

// has_value というメタ関数名で定義
BOOST_TTI_TRAIT_HAS_MEMBER_DATA(has_value, value)

struct X{
    int value;
};

BOOST_MPL_ASSERT((boost::tti::has_value<X, int>));

[::type を展開する]

さて、メタプログラミングを行なっている時に次のように ::type を展開して欲しい場合があると思います。

#include <boost/tti/has_type.hpp>

BOOST_TTI_HAS_TYPE(value_type)

template<typename T>
struct element_value_type{
    typedef typename T::value_type type;
};

template<typename T>
struct X
    // <mpl::identity<T>::type, element_type<T>::type> と展開して欲しい
    : has_type_value_type<mpl::identity<T>, element_value_type<T> >{};


残念ながら has_type_value_type 内では ::type の展開を行いません。
そこで、Boost.TTI で用意されているヘルパ関数を使用します。

#include <boost/tti/has_type.hpp>
// 追加
#include <boost/tti/mf/mf_has_type.hpp>

BOOST_TTI_HAS_TYPE(value_type)

template<typename T>
struct element_value_type{
    typedef typename T::value_type type;
};

template<typename T>
struct X
    // boost::tti::mf_has_type
    // Boost.MPL の placeholders を使用して部分適用される
    : boost::tti::mf_has_type<
        has_type_value_type<mpl::_1, mpl::_2>,
        mpl::identity<T>,
        element_value_type<T>
    >
{};

これでhas_type_value_type に ::type を展開した結果が適用されます。

[実際に使ってみる]

次のような関数を定義してみたいと思います。


1.T が value_type 型の value をメンバとして持っている場合は、value を返す
2.それ以外の場合は、T を返す


実際に書いてみるとこんな感じになりました。

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/mpl/and.hpp>
#include <boost/tti/has_type.hpp>
#include <boost/tti/mf/mf_has_type.hpp>
#include <boost/tti/has_member_data.hpp>
#include <boost/tti/mf/mf_has_member_data.hpp>

namespace mpl = boost::mpl;

BOOST_TTI_HAS_TYPE(value_type)           // has_type_value_type
BOOST_TTI_HAS_MEMBER_DATA(value)         // has_member_data_value

template<typename T>
struct element_value_type{
    typedef typename T::value_type type;
};

template<typename T>
struct trivial_get_value :
    mpl::and_<
        boost::is_class<T>,
        has_type_value_type<T>,
        boost::tti::mf_has_member_data<
            has_member_data_value<mpl::_1, mpl::_2>,
            mpl::identity<T>,
            element_value_type<T>
        >
    >::type
{};

template<typename T>
typename T::value_type
get_value(T const& t, typename boost::enable_if<trivial_get_value<T> >::type* =0){
    return t.value;
}

template<typename T>
T const&
get_value(T const& t, typename boost::disable_if<trivial_get_value<T> >::type* =0){
    return t;
}


struct X{
    typedef int value_type;
    value_type value;
};

struct hoge{};


int
main(){
    X x = {4};
    int n = get_value(x);
    
    hoge h;
    get_value(h);
    get_value(0);
    return 0;
}


trivial_get_value で get_value の仕様を満たしているかどうかのチェックを行っています。

[注意]

Boost.TTI のメタ関数にクラス以外の型を渡すとコンパイルエラー(false が返ってくるわけではなく)になるので注意して下さい。
上記のソースもそのために boost::is_same でチェックを行っています。

[所感]

と、いう感じで触りだけですが実際に使ってみました。

type を展開する時に placeholders を使わないと行けなかったりと、ちょっと使いづらい部分もありますが、それでも自分で定義するよりはだいぶ簡単になったと思います。

一部の変態メタプログラマにとってはかなり使い勝手がよいライブラリといえるでしょう、
特に C++03 だと decltype を使用したテクニックが使えないので重宝すると思います。
Boost 1.49.0 辺りでリリースされると嬉しいですねー。