fusion/example/cookbook/do_the_bind.cpp
2009-02-22 06:36:52 +00:00

269 lines
8.9 KiB
C++

/*=============================================================================
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;
}