// Egtra 2012-01-10
// License: NYSL 0.9982 ( http://www.kmonos.net/nysl/ )

#include <boost/ref.hpp>

template<typename T>
struct remove_reference
{
	typedef T type;
};

template<typename T>
struct remove_reference<T&>
{
	typedef T type;
};

namespace bind_helper
{
	template<typename T, typename U>
	struct pair
	{
		typedef T first_type;
		typedef U second_type;

		// このremove_referenceがstd::pairとの違い
		pair(typename remove_reference<T>::type const& f, typename remove_reference<U>::type const& s) : first(f), second(s) {}
		pair(pair const& rhs) : first(rhs.first), second(rhs.second) {}

		T first;
		U second;
	};

	template<typename T, typename U>
	inline pair<T, U> make_pair(T const& f, U const& s)
	{
		return pair<T, U>(f, s);
	}
}

using boost::cref;

namespace detail
{
	template<int i> struct placeholder {};
}

namespace placeholders
{
	detail::placeholder<0> const _1 = {};
	detail::placeholder<1> const _2 = {};
	detail::placeholder<2> const _3 = {};
}

namespace detail
{
	struct nil_t {};

	template<typename F, typename Arg0>
	inline typename F::result_type
	invoke(F const& f, bind_helper::pair<Arg0, nil_t> const& args)
	{
		return f(args.first);
	}

	template<typename F, typename Arg0, typename Arg1>
	inline typename F::result_type
	invoke(F const& f, bind_helper::pair<Arg0, bind_helper::pair<Arg1, nil_t>> const& args)
	{
		return f(args.first, args.second.first);
	}

	template<typename F, typename Arg0, typename Arg1, typename Arg2>
	inline typename F::result_type
	invoke(F const& f, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, detail::nil_t>>> const& args)
	{
		return f(args.first, args.second.first, args.second.second.first);
	}

	template<typename F, typename Arg0, typename Arg1, typename Arg2, typename Arg3>
	inline typename F::result_type
	invoke(F const& f, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, bind_helper::pair<Arg3, detail::nil_t>>>> const& args)
	{
		return f(args.first, args.second.first, args.second.second.first, args.second.second.second.first);
	}

	template<typename F, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
	inline typename F::result_type
	invoke(F const& f, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, bind_helper::pair<Arg3, bind_helper::pair<Arg4, detail::nil_t>>>>> const& args)
	{
		return f(args.first, args.second.first, args.second.second.first, args.second.second.second.first, args.second.second.second.second.first);
	}

	template<typename F, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
	inline typename F::result_type
	invoke(F const& f, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, bind_helper::pair<Arg3, bind_helper::pair<Arg4, bind_helper::pair<Arg5, detail::nil_t>>>>>> const& args)
	{
		return f(args.first, args.second.first, args.second.second.first, args.second.second.second.first, args.second.second.second.second.first, args.second.second.second.second.second.first);
	}

	template<int N, typename Pair>
	struct get
	{
		typedef get<N - 1, typename Pair::second_type> next_type;
		typedef typename next_type::result_type result_type;
		static result_type do_get(Pair const& x)
		{
			return next_type::do_get(x.second);
		}
	};

	template<typename Pair>
	struct get<0, Pair>
	{
		typedef typename Pair::first_type const& result_type;
		static result_type do_get(Pair const& x)
		{
			return x.first;
		}
	};

	template<typename Pair, typename Args>
	struct replace_impl;

	template<typename Args>
	struct replace_impl<nil_t, Args>
	{
		typedef nil_t result_type;
		static result_type do_replace(nil_t, Args const&)
		{
			return nil_t();
		}
	};

	template<int N, typename Cdr, typename Args>
	struct replace_impl<bind_helper::pair<placeholder<N>, Cdr>, Args>
	{
		typedef bind_helper::pair<
			typename get<N, Args>::result_type,
			typename replace_impl<Cdr, Args>::result_type
		> result_type;
		static result_type do_replace(bind_helper::pair<placeholder<N>, Cdr> const& pair, Args const& args)
		{
			return result_type(
				get<N, Args>::do_get(args),
				replace_impl<Cdr, Args>::do_replace(pair.second, args));
		}
	};

	template<typename Car, typename Cdr, typename Args>
	struct replace_impl<bind_helper::pair<Car, Cdr>, Args>
	{
		typedef bind_helper::pair<
			Car,
			typename replace_impl<Cdr, Args>::result_type
		> result_type;
		static result_type do_replace(bind_helper::pair<Car, Cdr> const& pair, Args const& args)
		{
			return result_type(
				pair.first,
				replace_impl<Cdr, Args>::do_replace(pair.second, args));
		}
	};

	template<typename Pair, typename Args>
	inline typename replace_impl<Pair, Args>::result_type
	replace(Pair const& pair, Args const& args)
	{
		return replace_impl<Pair, Args>::do_replace(pair, args);
	}

	template<typename F, typename Args>
	struct binder
	{
		F functor;
		Args boundArgs;

		typedef typename F::result_type result_type;

		result_type operator ()() const
		{
			return detail::invoke(functor, boundArgs);
		}

		template<typename Arg0>
		result_type operator ()(Arg0 const& arg0) const
		{
			return detail::invoke(functor, detail::replace(boundArgs, bind_helper::make_pair(cref(arg0), nil_t())));
		}

		template<typename Arg0, typename Arg1>
		result_type operator ()(Arg0 const& arg0, Arg1 const& arg1) const
		{
			return detail::invoke(functor, detail::replace(boundArgs, bind_helper::make_pair(cref(arg0), bind_helper::make_pair(cref(arg1), nil_t()))));
		}

		template<typename Arg0, typename Arg1, typename Arg2>
		result_type operator ()(Arg0 const& arg0, Arg1 const& arg1, Arg2 const& arg2) const
		{
			return detail::invoke(functor, detail::replace(boundArgs, bind_helper::make_pair(cref(arg0), bind_helper::make_pair(cref(arg1), bind_helper::make_pair(cref(arg2), nil_t())))));
		}

		template<typename Arg0, typename Arg1, typename Arg2, typename Arg3>
		result_type operator ()(Arg0 const& arg0, Arg1 const& arg1, Arg2 const& arg2, Arg3 const& arg3) const
		{
			return detail::invoke(functor, detail::replace(boundArgs, bind_helper::make_pair(cref(arg0), bind_helper::make_pair(cref(arg1), bind_helper::make_pair(cref(arg2), bind_helper::make_pair(cref(arg3), nil_t()))))));
		}

		template<typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
		result_type operator ()(Arg0 const& arg0, Arg1 const& arg1, Arg2 const& arg2, Arg3 const& arg3, Arg4 const& arg4) const
		{
			return detail::invoke(functor, detail::replace(boundArgs, bind_helper::make_pair(cref(arg0), bind_helper::make_pair(cref(arg1), bind_helper::make_pair(cref(arg2), bind_helper::make_pair(cref(arg3), bind_helper::make_pair(cref(arg4), nil_t())))))));
		}

		template<typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
		result_type operator ()(Arg0 const& arg0, Arg1 const& arg1, Arg2 const& arg2, Arg3 const& arg3, Arg4 const& arg4, Arg5 const& arg5) const
		{
			return detail::invoke(functor, detail::replace(boundArgs, bind_helper::make_pair(cref(arg0), bind_helper::make_pair(cref(arg1), bind_helper::make_pair(cref(arg2), bind_helper::make_pair(cref(arg3), bind_helper::make_pair(cref(arg4), bind_helper::make_pair(cref(arg5), nil_t()))))))));
		}
	};
}

template<typename Functor, typename Arg0>
detail::binder<Functor, bind_helper::pair<Arg0, detail::nil_t>>
bind(Functor const& f, Arg0 const& arg0)
{
	detail::binder<Functor, bind_helper::pair<Arg0, detail::nil_t>> ret =
	{
		f,
		bind_helper::make_pair(arg0, detail::nil_t()),
	};
	return ret;
}

template<typename Functor, typename Arg0, typename Arg1>
detail::binder<Functor, bind_helper::pair<Arg0, bind_helper::pair<Arg1, detail::nil_t>>>
bind(Functor const& f, Arg0 const& arg0, Arg1 const& arg1)
{
	detail::binder<Functor, bind_helper::pair<Arg0, bind_helper::pair<Arg1, detail::nil_t>>> ret =
	{
		f,
		bind_helper::make_pair(arg0, bind_helper::make_pair(arg1, detail::nil_t())),
	};
	return ret;
}

template<typename Functor, typename Arg0, typename Arg1, typename Arg2>
detail::binder<Functor, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, detail::nil_t>>>>
bind(Functor const& f, Arg0 const& arg0, Arg1 const& arg1, Arg2 const& arg2)
{
	detail::binder<Functor, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, detail::nil_t>>>> ret =
	{
		f,
		bind_helper::make_pair(arg0, bind_helper::make_pair(arg1, bind_helper::make_pair(arg2, detail::nil_t()))),
	};
	return ret;
}

template<typename Functor, typename Arg0, typename Arg1, typename Arg2, typename Arg3>
detail::binder<Functor, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, bind_helper::pair<Arg3, detail::nil_t>>>>>
bind(Functor const& f, Arg0 const& arg0, Arg1 const& arg1, Arg2 const& arg2, Arg3 const& arg3)
{
	detail::binder<Functor, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, bind_helper::pair<Arg3, detail::nil_t>>>>> ret =
	{
		f,
		bind_helper::make_pair(arg0, bind_helper::make_pair(arg1, bind_helper::make_pair(arg2, bind_helper::make_pair(arg3, detail::nil_t())))),
	};
	return ret;
}

template<typename Functor, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
detail::binder<Functor, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, bind_helper::pair<Arg3, bind_helper::pair<Arg4, detail::nil_t>>>>>>
bind(Functor const& f, Arg0 const& arg0, Arg1 const& arg1, Arg2 const& arg2, Arg3 const& arg3, Arg4 const& arg4)
{
	detail::binder<Functor, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, bind_helper::pair<Arg3, bind_helper::pair<Arg4, detail::nil_t>>>>>> ret =
	{
		f,
		bind_helper::make_pair(arg0, bind_helper::make_pair(arg1, bind_helper::make_pair(arg2, bind_helper::make_pair(arg3, bind_helper::make_pair(arg4, detail::nil_t()))))),
	};
	return ret;
}

template<typename Functor, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
detail::binder<Functor, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, bind_helper::pair<Arg3, bind_helper::pair<Arg4, bind_helper::pair<Arg5, detail::nil_t>>>>>>>
bind(Functor const& f, Arg0 const& arg0, Arg1 const& arg1, Arg2 const& arg2, Arg3 const& arg3, Arg4 const& arg4, Arg5 const& arg5)
{
	detail::binder<Functor, bind_helper::pair<Arg0, bind_helper::pair<Arg1, bind_helper::pair<Arg2, bind_helper::pair<Arg3, bind_helper::pair<Arg4, bind_helper::pair<Arg5, detail::nil_t>>>>>>> ret =
	{
		f,
		bind_helper::make_pair(arg0, bind_helper::make_pair(arg1, bind_helper::make_pair(arg2, bind_helper::make_pair(arg3, bind_helper::make_pair(arg4, bind_helper::make_pair(arg5, detail::nil_t())))))),
	};
	return ret;
}

//---------------

#include <iostream>

using namespace placeholders;

struct f
{
	typedef void result_type;
	void operator ()(int a, int b, int c, int d, int e, int f) const
	{
		std::cout << a << std::endl;
		std::cout << b << std::endl;
		std::cout << c << std::endl;
		std::cout << d << std::endl;
		std::cout << e << std::endl;
		std::cout << f << std::endl;
	}
};

int main()
{
	bind(f(), 1, 2, 3, _3, _2, _1)(10, 20, 30);
}

