型Fからboost::function<R(Args...)>を作る

BoostML - 【function/bind】 function object type introspection

template <class F>
void foo(F f)
{
    boost::function<???> ff = f;
}

みたいなことがしたいんだけど、どうすればいいの、というお話。


MLではBoost.FunctionTypesで関数(オブジェクト)から

  • 戻り値の型:result_type
  • パラメータパック:parameter_types

で取り出せばいいんじゃない、という回答がついてますが、
parameter_typesが返すのはMPLのシーケンスなので、それを展開するのはなかなか難しかったりします。


先のエントリで作成したfusion::vectorからstd::tupleへ変換するfuvector_to_tupleメタ関数を使用して
MPLのシーケンスをstd::tupleに変換し、可変引数テンプレートで型を取り出してみました。

#include <iostream>
#include <tuple>
#include <boost/function.hpp>
#include <boost/type_traits.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/function_types/result_type.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/value_at.hpp>
#include <boost/fusion/include/size.hpp>
#include <boost/fusion/include/pop_front.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/adapted/mpl.hpp>


template <class List, class FVector, int N>
struct fuvector_to_tuple_impl;

template <class... Args, class FVector, int N>
struct fuvector_to_tuple_impl<std::tuple<Args...>, FVector, N> {
    typedef typename fuvector_to_tuple_impl<
            std::tuple<Args...,
                       typename boost::fusion::result_of::value_at<FVector, boost::mpl::int_<0>>::type>,
            typename boost::fusion::result_of::pop_front<FVector>::type,
            N - 1
        >::type
    type;
};

template <class... Args, class FVector>
struct fuvector_to_tuple_impl<std::tuple<Args...>, FVector, 0> {
    typedef boost::tuple<Args...> type;
};


template <class FVector>
struct fuvector_to_tuple {
    typedef typename fuvector_to_tuple_impl<
            std::tuple<>,
            FVector,
            boost::fusion::result_of::size<FVector>::value
        >::type
    type;
};


template <class Mask>
struct get_parameter;

template <class Vector, int Size>
struct get_parameter<boost::mpl::v_mask<Vector, Size>> {
    typedef typename boost::mpl::pop_front<Vector>::type type;
};



template <class R, class Args>
struct to_function_impl;

template <class R, class... Args>
struct to_function_impl<R, std::tuple<Args...>> {
    typedef std::function<R(Args...)> type;
};

template <class F>
struct to_function {
private:
    typedef typename boost::function_types::result_type<F>::type result_type;

    typedef typename boost::function_types::parameter_types<F>::type            parameter;
    typedef typename get_parameter<parameter>::type                             masked_types;
    typedef typename boost::fusion::result_of::as_vector<masked_types>::type    vector_parameter;
    typedef typename fuvector_to_tuple<vector_parameter>::type                  parameter_types;

public:
    typedef typename to_function_impl<result_type, parameter_types>::type type;
};


template <class F>
void foo(F f)
{
    typedef typename to_function<F>::type function_type;

    static_assert(boost::is_same<function_type, boost::function<void(int)>>::value, "not same");
}

void a(int) {}


int main()
{
    foo(a);
}


追記:
2009/10/06 16:37 間違ったコードを掲載してしまっていたので、差し替えました。


追記2:
boost::function_types::function_typeを使うと
mpl::vectorから関数型を作れるそうなので、これでいいみたいです。

#include <boost/static_assert.hpp>
#include <boost/function.hpp>
#include <boost/type_traits.hpp>
#include <boost/function_types/function_type.hpp>
#include <boost/function_types/parameter_types.hpp>

template <class Mask>
struct get_parameter;

template <class Vector, int Size>
struct get_parameter<boost::mpl::v_mask<Vector, Size> > {
    typedef Vector type;
};

template <class F>
struct to_function {
private:
    typedef typename boost::function_types::function_type<
        typename get_parameter<
            typename boost::function_types::parameter_types<F>::type
        >::type
    >::type signature;

public:
    typedef boost::function<signature> type;
};


template <class F>
void foo(F f)
{
    typedef typename to_function<F>::type function_type;

    BOOST_STATIC_ASSERT((boost::is_same<function_type, boost::function<void(int)> >::value));
}

void a(int) {}


int main()
{
    foo(a);
}

ついでに、可変引数テンプレートが必要なくなったので
C++03のコードにしました。


追記3:
まだ間違ってたっぽい。
v_maskはBOOST_MPL_CFG_TYPEOF_BASED_SEQUENCESがdefineされてるときのみ定義されるので
直接使ってはいけない(by id:uskz)のと、VC9でやったらv_maskがないと言われてエラーになり
ごにょごにょいじってたらこうなりました。

#include <boost/static_assert.hpp>
#include <boost/function.hpp>
#include <boost/type_traits.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/function_types/function_type.hpp>
#include <boost/function_types/result_type.hpp>
#include <boost/function_types/parameter_types.hpp>

template <class F>
struct to_function {
private:
    typedef typename boost::function_types::result_type<F>::type result_type;
    typedef typename boost::function_types::parameter_types<F>::type parameters;
    typedef typename boost::mpl::push_front<parameters, result_type>::type pack;
    typedef typename boost::function_types::function_type<pack>::type signature;

public:
    typedef boost::function<signature> type;
};


template <class F>
void foo(F f)
{
    typedef typename to_function<F>::type function_type;

    BOOST_STATIC_ASSERT((boost::is_same<function_type, boost::function<void(int)> >::value));
}

void a(int) {}


int main()
{
    foo(a);
}


追記4:
boost::function_types::componentsを使って最終的にこうなりました。

#include <boost/static_assert.hpp>
#include <boost/function.hpp>
#include <boost/type_traits.hpp>
#include <boost/function_types/function_type.hpp>
#include <boost/function_types/components.hpp>

template <class F>
struct to_function {
private:
    typedef typename boost::function_types::components<F>::type components;
    typedef typename boost::function_types::function_type<components>::type signature;

public:
    typedef boost::function<signature> type;
};


template <class F>
void foo(F f)
{
    typedef typename to_function<F>::type function_type;

    BOOST_STATIC_ASSERT((boost::is_same<function_type, boost::function<void(int)> >::value));
}

void a(int) {}


int main()
{
    foo(a);
}