カリー化手直し

http://d.hatena.ne.jp/DigitalGhost/20080706/1215324721 を手直し。全然テストしてないけど。

  • 9引数まで対応
  • 適当に引数を束縛してからBoost.Functionとかに放りこめるようにした

currying.hpp


#if !defined CURRYING_HPP_INCLUDED_
#define CURRYING_HPP_INCLUDED_

#include <boost/type_traits/function_traits.hpp>
#include <boost/type_traits/is_function.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/size_t.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/utility/result_of.hpp>
#include <boost/function.hpp>

namespace exist {

namespace currying_detail {

template<typename T>
struct always_true : public boost::mpl::bool_<true> {};

template<typename R, typename BindArg, typename Base, unsigned int rest>
struct currying_t : public Base {
typedef Base base;
using Base::apply;

template<typename F_>
struct result {
typedef currying_t<R, typename boost::function_traits<F_>::arg1_type, currying_t, rest - 1> type;
};

BindArg arg_;

template<typename A>
currying_t<R, A, currying_t, rest - 1> operator()(A a) {
return currying_t<R, A, currying_t, rest - 1>(*this, a);
}

template<typename A1, typename A2>
R operator()(A1 const & a1, A2 const & a2) {
return currying_t<R, A1, currying_t, rest - 1>(*this, a1)(a2);
}
template<typename A1, typename A2, typename A3>
R operator()(A1 const & a1, A2 const & a2, A3 const & a3) {
return currying_t<R, A1, currying_t, rest - 1>(*this, a1)(a2, a3);
}
template<typename A1, typename A2, typename A3, typename A4>
R operator()(A1 const & a1, A2 const & a2, A3 const & a3, A4 const & a4) {
return currying_t<R, A1, currying_t, rest - 1>(*this, a1)(a2, a3, a4);
}
template<typename A1, typename A2, typename A3, typename A4, typename A5>
R operator()(A1 const & a1, A2 const & a2, A3 const & a3, A4 const & a4, A5 const & a5) {
return currying_t<R, A1, currying_t, rest - 1>(*this, a1)(a2, a3, a4, a5);
}
template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
R operator()(A1 const & a1, A2 const & a2, A3 const & a3, A4 const & a4, A5 const & a5, A6 const & a6) {
return currying_t<R, A1, currying_t, rest - 1>(*this, a1)(a2, a3, a4, a5, a6);
}
template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
R operator()(A1 const & a1, A2 const & a2, A3 const & a3, A4 const & a4, A5 const & a5, A6 const & a6, A7 const & a7) {
return currying_t<R, A1, currying_t, rest - 1>(*this, a1)(a2, a3, a4, a5, a6, a7);
}
template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
R operator()(A1 const & a1, A2 const & a2, A3 const & a3, A4 const & a4, A5 const & a5, A6 const & a6, A7 const & a7, A8 const & a8) {
return currying_t<R, A1, currying_t, rest - 1>(*this, a1)(a2, a3, a4, a5, a6, a7, a8);
}
template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
R operator()(A1 const & a1, A2 const & a2, A3 const & a3, A4 const & a4, A5 const & a5, A6 const & a6, A7 const & a7, A8 const & a8, A9 const & a9) {
return currying_t<R, A1, currying_t, rest - 1>(*this, a1)(a2, a3, a4, a5, a6, a7, a8, a9);
}

currying_t(Base const & base, BindArg const & arg) : Base(base), arg_(arg) {}
};

template<typename R, typename BindArg, typename Base>
struct currying_t<R, BindArg, Base, 1> : public Base {
typedef Base base;
typedef R result_type;

BindArg arg_;

template<typename A>
result_type operator()(A a) {
return Base::template apply<currying_t>(a);
}

currying_t(Base const & base, BindArg const & arg) : Base(base), arg_(arg) {}
};

template<typename R, typename F, size_t rest>
struct begin_currying {
template<typename F_>
struct result {
typedef currying_t<R, typename boost::function_traits<F_>::arg1_type, begin_currying, rest - 1> type;
};

F f_;

template<typename A>
currying_t<R, A, begin_currying, rest - 1> operator()(A const & a) {
return currying_t<R, A, begin_currying, rest - 1>(*this, a);
}

template<typename Args, typename A>
typename boost::enable_if<boost::mpl::and_<always_true<Args>, boost::mpl::equal_to<boost::mpl::int_<rest>, boost::mpl::int_<2> > >, R>::type
apply(A const & a) {
Args& self = static_cast<Args&>(*this);
return f_(self.arg_,
a);
}
template<typename Args, typename A>
typename boost::enable_if<boost::mpl::and_<always_true<Args>, boost::mpl::equal_to<boost::mpl::int_<rest>, boost::mpl::int_<3> > >, R>::type
apply(A const & a) {
Args& self = static_cast<Args&>(*this);
return f_(self.Args::base::arg_,
self.arg_,
a);
}
template<typename Args, typename A>
typename boost::enable_if<boost::mpl::and_<always_true<Args>, boost::mpl::equal_to<boost::mpl::int_<rest>, boost::mpl::int_<4> > >, R>::type
apply(A const & a) {
Args& self = static_cast<Args&>(*this);
return f_(self.Args::base::base::arg_,
self.Args::base::arg_,
self.arg_,
a);
}
template<typename Args, typename A>
typename boost::enable_if<boost::mpl::and_<always_true<Args>, boost::mpl::equal_to<boost::mpl::int_<rest>, boost::mpl::int_<5> > >, R>::type
apply(A const & a) {
Args& self = static_cast<Args&>(*this);
return f_(self.Args::base::base::base::arg_,
self.Args::base::base::arg_,
self.Args::base::arg_,
self.arg_,
a);
}
template<typename Args, typename A>
typename boost::enable_if<boost::mpl::and_<always_true<Args>, boost::mpl::equal_to<boost::mpl::int_<rest>, boost::mpl::int_<6> > >, R>::type
apply(A const & a) {
Args& self = static_cast<Args&>(*this);
return f_(self.Args::base::base::base::base::arg_,
self.Args::base::base::base::arg_,
self.Args::base::base::arg_,
self.Args::base::arg_,
self.arg_,
a);
}
template<typename Args, typename A>
typename boost::enable_if<boost::mpl::and_<always_true<Args>, boost::mpl::equal_to<boost::mpl::int_<rest>, boost::mpl::int_<7> > >, R>::type
apply(A const & a) {
Args& self = static_cast<Args&>(*this);
return f_(self.Args::base::base::base::base::base::arg_,
self.Args::base::base::base::base::arg_,
self.Args::base::base::base::arg_,
self.Args::base::base::arg_,
self.Args::base::arg_,
self.arg_,
a);
}
template<typename Args, typename A>
typename boost::enable_if<boost::mpl::and_<always_true<Args>, boost::mpl::equal_to<boost::mpl::int_<rest>, boost::mpl::int_<8> > >, R>::type
apply(A const & a) {
Args& self = static_cast<Args&>(*this);
return f_(self.Args::base::base::base::base::base::base::arg_,
self.Args::base::base::base::base::base::arg_,
self.Args::base::base::base::base::arg_,
self.Args::base::base::base::arg_,
self.Args::base::base::arg_,
self.Args::base::arg_,
self.arg_,
a);
}
template<typename Args, typename A>
typename boost::enable_if<boost::mpl::and_<always_true<Args>, boost::mpl::equal_to<boost::mpl::int_<rest>, boost::mpl::int_<9> > >, R>::type
apply(A const & a) {
Args& self = static_cast<Args&>(*this);
return f_(self.Args::base::base::base::base::base::base::base::arg_,
self.Args::base::base::base::base::base::base::arg_,
self.Args::base::base::base::base::base::arg_,
self.Args::base::base::base::base::arg_,
self.Args::base::base::base::arg_,
self.Args::base::base::arg_,
self.Args::base::arg_,
self.arg_,
a);
}

begin_currying(F const & f) : f_(f) {}
};

template<typename R, typename F>
struct begin_currying<R, F, 1> {
typedef R result_type;

F f_;

template<typename A>
result_type operator()(A const & a) {
return f_(a);
}

begin_currying(F const & f) : f_(f) {}
};

template<typename Signature, typename F>
struct currying_type {
typedef
begin_currying<typename boost::function_traits<Signature>::result_type,
F,
boost::function_traits<Signature>::arity>
type;
};

} // namespace currying_detail

template<typename F>
typename boost::enable_if<
boost::is_function<F>,
typename currying_detail::currying_type<F, F*>::type>::type
currying(F* f) {
return typename currying_detail::currying_type<F, F*>::type(f);
}

template<typename Signature, typename F>
typename boost::disable_if<
boost::is_function<F>,
typename currying_detail::currying_type<Signature, F> >::type
currying(F const & f) {
return typename currying_detail::currying_type<Signature, F>::type(f);
}

template<typename Signature>
typename currying_detail::currying_type<Signature, boost::function<Signature> >::type
currying(boost::function<Signature> const & f) {
return typename currying_detail::currying_type<Signature, boost::function<Signature> >::type(f);
}

} // namespace exist

#endif // CURRYING_HPP_INCLUDED_

#include "currying.hpp"
#include <boost/function.hpp>
#include <iostream>

int add(int a, int b) {
return a + b;
}

int twice(int n) {
return n * 2;
}

int main() {
using exist::currying;
std::cout << currying(twice)(3) << "\n"; // 6
std::cout << currying(add)(1)(2) << "\n"; // 3
boost::function<int(int)> f = currying(add)(1);
std::cout << f(3) << std::endl; // 4
return 0;
}