• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*=============================================================================
2     Copyright (c) 2006-2007 Tobias Schwinger
3 
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).
7 
8     Problem:
9 
10     How to "do the Bind?"
11 
12     This recipe shows how to implement a function binder, similar to
13     Boost.Bind based on the Functional module of Fusion.
14 
15     It works as follows:
16 
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
22     form.
23 ==============================================================================*/
24 
25 
26 #include <boost/fusion/functional/invocation/invoke.hpp>
27 #include <boost/fusion/functional/adapter/unfused.hpp>
28 #include <boost/fusion/support/deduce_sequence.hpp>
29 
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>
37 
38 #include <boost/functional/forward_adapter.hpp>
39 #include <boost/functional/lightweight_forward_adapter.hpp>
40 
41 #include <boost/type_traits/remove_reference.hpp>
42 
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>
48 
49 #include <boost/ref.hpp>
50 #include <iostream>
51 #include <typeinfo>
52 
53 namespace impl
54 {
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::_;
60 
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> { };
64 
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_   { };
71 
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
77     {
78         FinalArgs const & ref_final_args;
79     public:
80 
argument_transformimpl::argument_transform81         explicit argument_transform(FinalArgs const & final_args)
82             : ref_final_args(final_args)
83         { }
84 
85         // A placeholder? Replace it with an argument from the final call...
86         template <int Index>
87         inline typename result_of::at_c<FinalArgs const, Index>::type
operator ()impl::argument_transform88         operator()(placeholder<Index> const &) const
89         {
90             return fusion::at_c<Index>(this->ref_final_args);
91         }
92         // ...just return the bound argument, otherwise.
operator ()impl::argument_transform93         template <typename T> inline T & operator()(T & bound) const
94         {
95             return bound;
96         }
97 
98         template <typename Signature>
99         struct result;
100 
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>,
105                 mpl::identity<T>
106             >
107         { };
108     };
109 
110     // Fused implementation of the bound function, the function object
111     // returned by bind
112     template <class BindArgs> class fused_bound_function
113     {
114         // Transform arguments to be held by value
115         typedef typename traits::deduce_sequence<BindArgs>::type bound_args;
116 
117         bound_args fsq_bind_args;
118     public:
119 
fused_bound_function(BindArgs const & bind_args)120         fused_bound_function(BindArgs const & bind_args)
121           : fsq_bind_args(bind_args)
122         { }
123 
124         template <typename Signature>
125         struct result;
126 
127         template <class FinalArgs>
128         struct result_impl
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
133                 >::type
134             >
135         { };
136 
137         template <class Self, class FinalArgs>
138         struct result< Self (FinalArgs) >
139             : result_impl< typename boost::remove_reference<FinalArgs>::type >
140         { };
141 
142         template <class FinalArgs>
143         inline typename result_impl<FinalArgs>::type
operator ()(FinalArgs const & final_args) const144         operator()(FinalArgs const & final_args) const
145         {
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) ) );
149         }
150         // Could add a non-const variant - omitted for readability
151 
152     };
153 
154     // Find the number of placeholders in use
155     struct n_placeholders
156     {
157         struct fold_op
158         {
159             template <typename Sig> struct result;
160             template <class S, class A, class B> struct result< S(A &,B &) >
161                 : mpl::max<A,B> { };
162         };
163         struct filter_pred
164         {
165             template <class X> struct apply : is_placeholder<X> { };
166         };
167 
168         template <typename Seq>
169         struct apply
170             : mpl::next< typename result_of::fold<
171                 fusion::filter_view<Seq,filter_pred>, mpl::int_<-1>, fold_op
172             >::type>::type
173         { };
174     };
175 
176     // Fused implementation of the 'bind' function
177     struct fused_binder
178     {
179         template <class Signature>
180         struct result;
181 
182         template <class BindArgs,
183             int Placeholders = n_placeholders::apply<BindArgs>::value>
184         struct result_impl
185         {
186             typedef boost::forward_adapter<fusion::unfused<
187                 fused_bound_function<BindArgs>,!Placeholders>,Placeholders> type;
188         };
189 
190         template <class Self, class BindArgs>
191         struct result< Self (BindArgs) >
192             : result_impl< typename boost::remove_reference<BindArgs>::type >
193         { };
194 
195         template <class BindArgs>
196         inline typename result_impl< BindArgs >::type
operator ()impl::fused_binder197         operator()(BindArgs & bind_args) const
198         {
199             return typename result< void(BindArgs) >::type(
200                 fusion::unfused< fused_bound_function<BindArgs>,
201                     ! n_placeholders::apply<BindArgs>::value >(bind_args) );
202         }
203     };
204 
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;
209 }
210 
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>();
216 
217 // The bind function is a global, too
218 impl::binder const bind = impl::binder();
219 
220 
221 // OK, let's try it out:
222 
223 struct func
224 {
225     typedef int result_type;
226 
operator ()func227     inline int operator()() const
228     {
229         std::cout << "operator()" << std::endl;
230         return 0;
231     }
232 
233     template <typename A>
operator ()func234     inline int operator()(A const & a) const
235     {
236         std::cout << "operator()(A const & a)" << std::endl;
237         std::cout << "  a = " << a << "  A = " << typeid(A).name() << std::endl;
238         return 1;
239     }
240 
241     template <typename A, typename B>
operator ()func242     inline int operator()(A const & a, B & b) const
243     {
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;
247         return 2;
248     }
249 };
250 
main()251 int main()
252 {
253     func f;
254     int value = 42;
255     using boost::ref;
256 
257     int errors = 0;
258 
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);
265 
266     return !! errors;
267 }
268 
269