/*============================================================================= Copyright (c) 2006-2007 Tobias Schwinger Use modification and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt). Problem: How to "do the Bind?" This recipe shows how to implement a function binder, similar to Boost.Bind based on the Functional module of Fusion. It works as follows: 'bind' is a global, stateless function object. It is implemented in fused form (fused_binder) and transformed into a variadic function object. When called, 'bind' returns another function object, which holds the arguments of the call to 'bind'. It is, again, implemented in fused form (fused_bound_function) and transformed into unfused form. ==============================================================================*/ #include <boost/fusion/functional/invocation/invoke.hpp> #include <boost/fusion/functional/adapter/unfused.hpp> #include <boost/fusion/support/deduce_sequence.hpp> #include <boost/fusion/sequence/intrinsic/at.hpp> #include <boost/fusion/sequence/intrinsic/front.hpp> #include <boost/fusion/sequence/intrinsic/size.hpp> #include <boost/fusion/algorithm/transformation/transform.hpp> #include <boost/fusion/algorithm/transformation/pop_front.hpp> #include <boost/fusion/algorithm/iteration/fold.hpp> #include <boost/fusion/view/filter_view.hpp> #include <boost/functional/forward_adapter.hpp> #include <boost/functional/lightweight_forward_adapter.hpp> #include <boost/type_traits/remove_reference.hpp> #include <boost/mpl/eval_if.hpp> #include <boost/mpl/identity.hpp> #include <boost/mpl/int.hpp> #include <boost/mpl/max.hpp> #include <boost/mpl/next.hpp> #include <boost/ref.hpp> #include <iostream> #include <typeinfo> namespace impl { namespace fusion = boost::fusion; namespace traits = boost::fusion::traits; namespace result_of = boost::fusion::result_of; namespace mpl = boost::mpl; using mpl::placeholders::_; // Placeholders (we inherit from mpl::int_, so we can use placeholders // as indices for fusion::at, later) template <int I> struct placeholder : mpl::int_<I> { }; // A traits class to find out whether T is a placeholeder template <typename T> struct is_placeholder : mpl::false_ { }; template <int I> struct is_placeholder< placeholder<I> > : mpl::true_ { }; template <int I> struct is_placeholder< placeholder<I> & > : mpl::true_ { }; template <int I> struct is_placeholder< placeholder<I> const > : mpl::true_ { }; template <int I> struct is_placeholder< placeholder<I> const & > : mpl::true_ { }; // This class template provides a Polymorphic Function Object to be used // with fusion::transform. It is applied to the sequence of arguments that // describes the binding and holds a reference to the sequence of arguments // from the final call. template<class FinalArgs> struct argument_transform { FinalArgs const & ref_final_args; public: explicit argument_transform(FinalArgs const & final_args) : ref_final_args(final_args) { } // A placeholder? Replace it with an argument from the final call... template <int Index> inline typename result_of::at_c<FinalArgs const, Index>::type operator()(placeholder<Index> const &) const { return fusion::at_c<Index>(this->ref_final_args); } // ...just return the bound argument, otherwise. template <typename T> inline T & operator()(T & bound) const { return bound; } template <typename Signature> struct result; template <class Self, typename T> struct result< Self (T) > : mpl::eval_if< is_placeholder<T>, result_of::at<FinalArgs,typename boost::remove_reference<T>::type>, mpl::identity<T> > { }; }; // Fused implementation of the bound function, the function object // returned by bind template <class BindArgs> class fused_bound_function { // Transform arguments to be held by value typedef typename traits::deduce_sequence<BindArgs>::type bound_args; bound_args fsq_bind_args; public: fused_bound_function(BindArgs const & bind_args) : fsq_bind_args(bind_args) { } template <typename Signature> struct result; template <class FinalArgs> struct result_impl : result_of::invoke< typename result_of::front<bound_args>::type, typename result_of::transform< typename result_of::pop_front<bound_args>::type, argument_transform<FinalArgs> const >::type > { }; template <class Self, class FinalArgs> struct result< Self (FinalArgs) > : result_impl< typename boost::remove_reference<FinalArgs>::type > { }; template <class FinalArgs> inline typename result_impl<FinalArgs>::type operator()(FinalArgs const & final_args) const { return fusion::invoke( fusion::front(this->fsq_bind_args), fusion::transform( fusion::pop_front(this->fsq_bind_args), argument_transform<FinalArgs>(final_args) ) ); } // Could add a non-const variant - omitted for readability }; // Find the number of placeholders in use struct n_placeholders { struct fold_op { template <typename Sig> struct result; template <class S, class A, class B> struct result< S(A &,B &) > : mpl::max<A,B> { }; }; struct filter_pred { template <class X> struct apply : is_placeholder<X> { }; }; template <typename Seq> struct apply : mpl::next< typename result_of::fold< fusion::filter_view<Seq,filter_pred>, mpl::int_<-1>, fold_op >::type>::type { }; }; // Fused implementation of the 'bind' function struct fused_binder { template <class Signature> struct result; template <class BindArgs, int Placeholders = n_placeholders::apply<BindArgs>::value> struct result_impl { typedef boost::forward_adapter<fusion::unfused< fused_bound_function<BindArgs>,!Placeholders>,Placeholders> type; }; template <class Self, class BindArgs> struct result< Self (BindArgs) > : result_impl< typename boost::remove_reference<BindArgs>::type > { }; template <class BindArgs> inline typename result_impl< BindArgs >::type operator()(BindArgs & bind_args) const { return typename result< void(BindArgs) >::type( fusion::unfused< fused_bound_function<BindArgs>, ! n_placeholders::apply<BindArgs>::value >(bind_args) ); } }; // The binder's unfused type. We use lightweght_forward_adapter to make // that thing more similar to Boost.Bind. Because of that we have to use // Boost.Ref (below in the sample code) typedef boost::lightweight_forward_adapter< fusion::unfused<fused_binder> > binder; } // Placeholder globals impl::placeholder<0> const _1_ = impl::placeholder<0>(); impl::placeholder<1> const _2_ = impl::placeholder<1>(); impl::placeholder<2> const _3_ = impl::placeholder<2>(); impl::placeholder<3> const _4_ = impl::placeholder<3>(); // The bind function is a global, too impl::binder const bind = impl::binder(); // OK, let's try it out: struct func { typedef int result_type; inline int operator()() const { std::cout << "operator()" << std::endl; return 0; } template <typename A> inline int operator()(A const & a) const { std::cout << "operator()(A const & a)" << std::endl; std::cout << " a = " << a << " A = " << typeid(A).name() << std::endl; return 1; } template <typename A, typename B> inline int operator()(A const & a, B & b) const { std::cout << "operator()(A const & a, B & b)" << std::endl; std::cout << " a = " << a << " A = " << typeid(A).name() << std::endl; std::cout << " b = " << b << " B = " << typeid(B).name() << std::endl; return 2; } }; int main() { func f; int value = 42; using boost::ref; int errors = 0; errors += !( bind(f)() == 0); errors += !( bind(f,"Hi")() == 1); errors += !( bind(f,_1_)("there.") == 1); errors += !( bind(f,"The answer is",_1_)(12) == 2); errors += !( bind(f,_1_,ref(value))("Really?") == 2); errors += !( bind(f,_1_,_2_)("Dunno. If there is an answer, it's",value) == 2); return !! errors; }