1 /*=============================================================================
2 Copyright (c) 2006-2007 Tobias Schwinger
4 Use modification and distribution are subject to the Boost Software
5 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 http://www.boost.org/LICENSE_1_0.txt).
12 This recipe shows how to implement a function binder, similar to
13 Boost.Bind based on the Functional module of Fusion.
17 'bind' is a global, stateless function object. It is implemented in
18 fused form (fused_binder) and transformed into a variadic function
19 object. When called, 'bind' returns another function object, which
20 holds the arguments of the call to 'bind'. It is, again, implemented
21 in fused form (fused_bound_function) and transformed into unfused
23 ==============================================================================*/
26 #include <boost/fusion/functional/invocation/invoke.hpp>
27 #include <boost/fusion/functional/adapter/unfused.hpp>
28 #include <boost/fusion/support/deduce_sequence.hpp>
30 #include <boost/fusion/sequence/intrinsic/at.hpp>
31 #include <boost/fusion/sequence/intrinsic/front.hpp>
32 #include <boost/fusion/sequence/intrinsic/size.hpp>
33 #include <boost/fusion/algorithm/transformation/transform.hpp>
34 #include <boost/fusion/algorithm/transformation/pop_front.hpp>
35 #include <boost/fusion/algorithm/iteration/fold.hpp>
36 #include <boost/fusion/view/filter_view.hpp>
38 #include <boost/functional/forward_adapter.hpp>
39 #include <boost/functional/lightweight_forward_adapter.hpp>
41 #include <boost/type_traits/remove_reference.hpp>
43 #include <boost/mpl/eval_if.hpp>
44 #include <boost/mpl/identity.hpp>
45 #include <boost/mpl/int.hpp>
46 #include <boost/mpl/max.hpp>
47 #include <boost/mpl/next.hpp>
49 #include <boost/ref.hpp>
55 namespace fusion
= boost::fusion
;
56 namespace traits
= boost::fusion::traits
;
57 namespace result_of
= boost::fusion::result_of
;
58 namespace mpl
= boost::mpl
;
59 using mpl::placeholders::_
;
61 // Placeholders (we inherit from mpl::int_, so we can use placeholders
62 // as indices for fusion::at, later)
63 template <int I
> struct placeholder
: mpl::int_
<I
> { };
65 // A traits class to find out whether T is a placeholeder
66 template <typename T
> struct is_placeholder
: mpl::false_
{ };
67 template <int I
> struct is_placeholder
< placeholder
<I
> > : mpl::true_
{ };
68 template <int I
> struct is_placeholder
< placeholder
<I
> & > : mpl::true_
{ };
69 template <int I
> struct is_placeholder
< placeholder
<I
> const > : mpl::true_
{ };
70 template <int I
> struct is_placeholder
< placeholder
<I
> const & > : mpl::true_
{ };
72 // This class template provides a Polymorphic Function Object to be used
73 // with fusion::transform. It is applied to the sequence of arguments that
74 // describes the binding and holds a reference to the sequence of arguments
75 // from the final call.
76 template<class FinalArgs
> struct argument_transform
78 FinalArgs
const & ref_final_args
;
81 explicit argument_transform(FinalArgs
const & final_args
)
82 : ref_final_args(final_args
)
85 // A placeholder? Replace it with an argument from the final call...
87 inline typename
result_of::at_c
<FinalArgs
const, Index
>::type
88 operator()(placeholder
<Index
> const &) const
90 return fusion::at_c
<Index
>(this->ref_final_args
);
92 // ...just return the bound argument, otherwise.
93 template <typename T
> inline T
& operator()(T
& bound
) const
98 template <typename Signature
>
101 template <class Self
, typename T
>
102 struct result
< Self (T
) >
103 : mpl::eval_if
< is_placeholder
<T
>,
104 result_of::at
<FinalArgs
,typename
boost::remove_reference
<T
>::type
>,
110 // Fused implementation of the bound function, the function object
112 template <class BindArgs
> class fused_bound_function
114 // Transform arguments to be held by value
115 typedef typename
traits::deduce_sequence
<BindArgs
>::type bound_args
;
117 bound_args fsq_bind_args
;
120 fused_bound_function(BindArgs
const & bind_args
)
121 : fsq_bind_args(bind_args
)
124 template <typename Signature
>
127 template <class FinalArgs
>
129 : result_of::invoke
< typename
result_of::front
<bound_args
>::type
,
130 typename
result_of::transform
<
131 typename
result_of::pop_front
<bound_args
>::type
,
132 argument_transform
<FinalArgs
> const
137 template <class Self
, class FinalArgs
>
138 struct result
< Self (FinalArgs
) >
139 : result_impl
< typename
boost::remove_reference
<FinalArgs
>::type
>
142 template <class FinalArgs
>
143 inline typename result_impl
<FinalArgs
>::type
144 operator()(FinalArgs
const & final_args
) const
146 return fusion::invoke( fusion::front(this->fsq_bind_args
),
147 fusion::transform( fusion::pop_front(this->fsq_bind_args
),
148 argument_transform
<FinalArgs
>(final_args
) ) );
150 // Could add a non-const variant - omitted for readability
154 // Find the number of placeholders in use
155 struct n_placeholders
159 template <typename Sig
> struct result
;
160 template <class S
, class A
, class B
> struct result
< S(A
&,B
&) >
165 template <class X
> struct apply
: is_placeholder
<X
> { };
168 template <typename Seq
>
170 : mpl::next
< typename
result_of::fold
<
171 fusion::filter_view
<Seq
,filter_pred
>, mpl::int_
<-1>, fold_op
176 // Fused implementation of the 'bind' function
179 template <class Signature
>
182 template <class BindArgs
,
183 int Placeholders
= n_placeholders::apply
<BindArgs
>::value
>
186 typedef boost::forward_adapter
<fusion::unfused
<
187 fused_bound_function
<BindArgs
>,!Placeholders
>,Placeholders
> type
;
190 template <class Self
, class BindArgs
>
191 struct result
< Self (BindArgs
) >
192 : result_impl
< typename
boost::remove_reference
<BindArgs
>::type
>
195 template <class BindArgs
>
196 inline typename result_impl
< BindArgs
>::type
197 operator()(BindArgs
& bind_args
) const
199 return typename result
< void(BindArgs
) >::type(
200 fusion::unfused
< fused_bound_function
<BindArgs
>,
201 ! n_placeholders::apply
<BindArgs
>::value
>(bind_args
) );
205 // The binder's unfused type. We use lightweght_forward_adapter to make
206 // that thing more similar to Boost.Bind. Because of that we have to use
207 // Boost.Ref (below in the sample code)
208 typedef boost::lightweight_forward_adapter
< fusion::unfused
<fused_binder
> > binder
;
211 // Placeholder globals
212 impl::placeholder
<0> const _1_
= impl::placeholder
<0>();
213 impl::placeholder
<1> const _2_
= impl::placeholder
<1>();
214 impl::placeholder
<2> const _3_
= impl::placeholder
<2>();
215 impl::placeholder
<3> const _4_
= impl::placeholder
<3>();
217 // The bind function is a global, too
218 impl::binder
const bind
= impl::binder();
221 // OK, let's try it out:
225 typedef int result_type
;
227 inline int operator()() const
229 std::cout
<< "operator()" << std::endl
;
233 template <typename A
>
234 inline int operator()(A
const & a
) const
236 std::cout
<< "operator()(A const & a)" << std::endl
;
237 std::cout
<< " a = " << a
<< " A = " << typeid(A
).name() << std::endl
;
241 template <typename A
, typename B
>
242 inline int operator()(A
const & a
, B
& b
) const
244 std::cout
<< "operator()(A const & a, B & b)" << std::endl
;
245 std::cout
<< " a = " << a
<< " A = " << typeid(A
).name() << std::endl
;
246 std::cout
<< " b = " << b
<< " B = " << typeid(B
).name() << std::endl
;
259 errors
+= !( bind(f
)() == 0);
260 errors
+= !( bind(f
,"Hi")() == 1);
261 errors
+= !( bind(f
,_1_
)("there.") == 1);
262 errors
+= !( bind(f
,"The answer is",_1_
)(12) == 2);
263 errors
+= !( bind(f
,_1_
,ref(value
))("Really?") == 2);
264 errors
+= !( bind(f
,_1_
,_2_
)("Dunno. If there is an answer, it's",value
) == 2);