/*============================================================================= 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 struct placeholder : mpl::int_ { }; // A traits class to find out whether T is a placeholeder template struct is_placeholder : mpl::false_ { }; template struct is_placeholder< placeholder > : mpl::true_ { }; template struct is_placeholder< placeholder & > : mpl::true_ { }; template struct is_placeholder< placeholder const > : mpl::true_ { }; template struct is_placeholder< placeholder 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 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 inline typename result_of::at_c::type operator()(placeholder const &) const { return fusion::at_c(this->ref_final_args); } // ...just return the bound argument, otherwise. template inline T & operator()(T & bound) const { return bound; } template struct result; template struct result< Self (T) > : mpl::eval_if< is_placeholder, result_of::at::type>, mpl::identity > { }; }; // Fused implementation of the bound function, the function object // returned by bind template class fused_bound_function { // Transform arguments to be held by value typedef typename traits::deduce_sequence::type bound_args; bound_args fsq_bind_args; public: fused_bound_function(BindArgs const & bind_args) : fsq_bind_args(bind_args) { } template struct result; template struct result_impl : result_of::invoke< typename result_of::front::type, typename result_of::transform< typename result_of::pop_front::type, argument_transform const >::type > { }; template struct result< Self (FinalArgs) > : result_impl< typename boost::remove_reference::type > { }; template inline typename result_impl::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(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 struct result; template struct result< S(A &,B &) > : mpl::max { }; }; struct filter_pred { template struct apply : is_placeholder { }; }; template struct apply : mpl::next< typename result_of::fold< fusion::filter_view, mpl::int_<-1>, fold_op >::type>::type { }; }; // Fused implementation of the 'bind' function struct fused_binder { template struct result; template ::value> struct result_impl { typedef boost::forward_adapter,!Placeholders>,Placeholders> type; }; template struct result< Self (BindArgs) > : result_impl< typename boost::remove_reference::type > { }; template inline typename result_impl< BindArgs >::type operator()(BindArgs & bind_args) const { return typename result< void(BindArgs) >::type( fusion::unfused< fused_bound_function, ! n_placeholders::apply::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 > 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 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 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; }