diff --git a/example/cookbook/do_the_bind.cpp b/example/cookbook/do_the_bind.cpp new file mode 100644 index 00000000..a391fa62 --- /dev/null +++ b/example/cookbook/do_the_bind.cpp @@ -0,0 +1,224 @@ +/*============================================================================= + 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 + +namespace impl +{ + namespace fusion = boost::fusion; + 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 + : 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 + { + BindArgs fsq_bind_args; + public: + + fused_bound_function(BindArgs const & bind_args) + : fsq_bind_args(bind_args) + { } + + template ::value && + !! mpl::count_if >::value) > + struct result + { }; + + template + struct result + : result_of::invoke< typename result_of::front::type, + typename result_of::transform< + typename result_of::pop_front::type, + argument_transform const + >::type + > + { }; + + template + inline typename result::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 + + }; + + // Fused implementation of the 'bind' function + struct fused_binder + { + template + struct result + { + // We have to transform the arguments so they are held by-value + // in the returned function. + typedef fusion::unfused_generic< fused_bound_function< + typename fusion::storable_arguments::type > > type; + }; + + template + inline typename result::type + operator()(BindArgs & bind_args) const + { + return typename result::type(bind_args); + } + }; + + // The binder's unfused type. We use unfused_rvalue_args to make that + // thing more similar to Boost.Bind. Because of that we have to use + // Boost.Ref (below in the sample code) + typedef fusion::unfused_rvalue_args 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_)(value) == 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; +} +