1 //  bind_tests_advanced.cpp  -- The Boost Lambda Library ------------------
2 //
3 // Copyright (C) 2000-2003 Jaakko Jarvi (jaakko.jarvi@cs.utu.fi)
4 // Copyright (C) 2000-2003 Gary Powell (powellg@amazon.com)
5 // Copyright (C) 2010 Steven Watanabe
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See
8 // accompanying file LICENSE_1_0.txt or copy at
9 // http://www.boost.org/LICENSE_1_0.txt)
10 //
11 // For more information, see www.boost.org
12 
13 // -----------------------------------------------------------------------
14 
15 
16 #include <boost/test/minimal.hpp>    // see "Header Implementation Option"
17 
18 #include "boost/lambda/lambda.hpp"
19 #include "boost/lambda/bind.hpp"
20 
21 
22 #include "boost/any.hpp"
23 #include "boost/type_traits/is_reference.hpp"
24 #include "boost/mpl/assert.hpp"
25 #include "boost/mpl/if.hpp"
26 
27 #include <iostream>
28 
29 #include <functional>
30 
31 #include <algorithm>
32 
33 
34 using namespace boost::lambda;
35 namespace bl = boost::lambda;
36 
sum_0()37 int sum_0() { return 0; }
sum_1(int a)38 int sum_1(int a) { return a; }
sum_2(int a,int b)39 int sum_2(int a, int b) { return a+b; }
40 
product_2(int a,int b)41 int product_2(int a, int b) { return a*b; }
42 
43 // unary function that returns a pointer to a binary function
44 typedef int (*fptr_type)(int, int);
sum_or_product(bool x)45 fptr_type sum_or_product(bool x) {
46   return x ? sum_2 : product_2;
47 }
48 
49 // a nullary functor that returns a pointer to a unary function that
50 // returns a pointer to a binary function.
51 struct which_one {
52   typedef fptr_type (*result_type)(bool x);
53   template <class T> struct sig { typedef result_type type; };
54 
operator ()which_one55   result_type operator()() const { return sum_or_product; }
56 };
57 
test_nested_binds()58 void test_nested_binds()
59 {
60   int j = 2; int k = 3;
61 
62 // bind calls can be nested (the target function can be a lambda functor)
63 // The interpretation is, that the innermost lambda functor returns something
64 // that is bindable (another lambda functor, function pointer ...)
65   bool condition;
66 
67   condition = true;
68   BOOST_CHECK(bind(bind(&sum_or_product, _1), 1, 2)(condition)==3);
69   BOOST_CHECK(bind(bind(&sum_or_product, _1), _2, _3)(condition, j, k)==5);
70 
71   condition = false;
72   BOOST_CHECK(bind(bind(&sum_or_product, _1), 1, 2)(condition)==2);
73   BOOST_CHECK(bind(bind(&sum_or_product, _1), _2, _3)(condition, j, k)==6);
74 
75 
76   which_one wo;
77   BOOST_CHECK(bind(bind(bind(wo), _1), _2, _3)(condition, j, k)==6);
78 
79 
80   return;
81 }
82 
83 
84 // unlambda -------------------------------------------------
85 
86   // Sometimes it may be necessary to prevent the argument substitution of
87   // taking place. For example, we may end up with a nested bind expression
88   // inadvertently when using the target function is received as a parameter
89 
90 template<class F>
call_with_100(const F & f)91 int call_with_100(const F& f) {
92 
93 
94 
95   //  bind(f, _1)(make_const(100));
96   // This would result in;
97   // bind(_1 + 1, _1)(make_const(100)) , which would be a compile time error
98 
99   return bl::bind(unlambda(f), _1)(make_const(100));
100 
101   // for other functors than lambda functors, unlambda has no effect
102   // (except for making them const)
103 }
104 
105 template<class F>
call_with_101(const F & f)106 int call_with_101(const F& f) {
107 
108   return bind(unlambda(f), _1)(make_const(101));
109 
110 }
111 
112 
test_unlambda()113 void test_unlambda() {
114 
115   int i = 1;
116 
117   BOOST_CHECK(unlambda(_1 + _2)(i, i) == 2);
118   BOOST_CHECK(unlambda(++var(i))() == 2);
119   BOOST_CHECK(call_with_100(_1 + 1) == 101);
120 
121 
122   BOOST_CHECK(call_with_101(_1 + 1) == 102);
123 
124   BOOST_CHECK(call_with_100(bl::bind(std_functor(std::bind1st(std::plus<int>(), 1)), _1)) == 101);
125 
126   // std_functor insturcts LL that the functor defines a result_type typedef
127   // rather than a sig template.
128   bl::bind(std_functor(std::plus<int>()), _1, _2)(i, i);
129 }
130 
131 
132 
133 
134 // protect ------------------------------------------------------------
135 
136 // protect protects a lambda functor from argument substitution.
137 // protect is useful e.g. with nested stl algorithm calls.
138 
139 namespace ll {
140 
141 struct for_each {
142 
143   // note, std::for_each returns it's last argument
144   // We want the same behaviour from our ll::for_each.
145   // However, the functor can be called with any arguments, and
146   // the return type thus depends on the argument types.
147 
148   // 1. Provide a sig class member template:
149 
150   // The return type deduction system instantiate this class as:
151   // sig<Args>::type, where Args is a boost::tuples::cons-list
152   // The head type is the function object type itself
153   // cv-qualified (so it is possilbe to provide different return types
154   // for differently cv-qualified operator()'s.
155 
156   // The tail type is the list of the types of the actual arguments the
157   // function was called with.
158   // So sig should contain a typedef type, which defines a mapping from
159   // the operator() arguments to its return type.
160   // Note, that it is possible to provide different sigs for the same functor
161   // if the functor has several operator()'s, even if they have different
162   // number of arguments.
163 
164   // Note, that the argument types in Args are guaranteed to be non-reference
165   // types, but they can have cv-qualifiers.
166 
167     template <class Args>
168   struct sig {
169     typedef typename boost::remove_const<
170           typename boost::tuples::element<3, Args>::type
171        >::type type;
172   };
173 
174   template <class A, class B, class C>
175   C
operator ()ll::for_each176   operator()(const A& a, const B& b, const C& c) const
177   { return std::for_each(a, b, c);}
178 };
179 
180 } // end of ll namespace
181 
test_protect()182 void test_protect()
183 {
184   int i = 0;
185   int b[3][5];
186   int* a[3];
187 
188   for(int j=0; j<3; ++j) a[j] = b[j];
189 
190   std::for_each(a, a+3,
191            bind(ll::for_each(), _1, _1 + 5, protect(_1 = ++var(i))));
192 
193   // This is how you could output the values (it is uncommented, no output
194   // from a regression test file):
195   //  std::for_each(a, a+3,
196   //                bind(ll::for_each(), _1, _1 + 5,
197   //                     std::cout << constant("\nLine ") << (&_1 - a) << " : "
198   //                     << protect(_1)
199   //                     )
200   //               );
201 
202   int sum = 0;
203 
204   std::for_each(a, a+3,
205            bind(ll::for_each(), _1, _1 + 5,
206                 protect(sum += _1))
207                );
208   BOOST_CHECK(sum == (1+15)*15/2);
209 
210   sum = 0;
211 
212   std::for_each(a, a+3,
213            bind(ll::for_each(), _1, _1 + 5,
214                 sum += 1 + protect(_1)) // add element count
215                );
216   BOOST_CHECK(sum == (1+15)*15/2 + 15);
217 
218   (1 + protect(_1))(sum);
219 
220   int k = 0;
221   ((k += constant(1)) += protect(constant(2)))();
222   BOOST_CHECK(k==1);
223 
224   k = 0;
225   ((k += constant(1)) += protect(constant(2)))()();
226   BOOST_CHECK(k==3);
227 
228   // note, the following doesn't work:
229 
230   //  ((var(k) = constant(1)) = protect(constant(2)))();
231 
232   // (var(k) = constant(1))() returns int& and thus the
233   // second assignment fails.
234 
235   // We should have something like:
236   // bind(var, var(k) = constant(1)) = protect(constant(2)))();
237   // But currently var is not bindable.
238 
239   // The same goes with ret. A bindable ret could be handy sometimes as well
240   // (protect(std::cout << _1), std::cout << _1)(i)(j); does not work
241   // because the comma operator tries to store the result of the evaluation
242   // of std::cout << _1 as a copy (and you can't copy std::ostream).
243   // something like this:
244   // (protect(std::cout << _1), bind(ref, std::cout << _1))(i)(j);
245 
246 
247   // the stuff below works, but we do not want extra output to
248   // cout, must be changed to stringstreams but stringstreams do not
249   // work due to a bug in the type deduction. Will be fixed...
250 #if 0
251   // But for now, ref is not bindable. There are other ways around this:
252 
253     int x = 1, y = 2;
254     (protect(std::cout << _1), (std::cout << _1, 0))(x)(y);
255 
256   // added one dummy value to make the argument to comma an int
257   // instead of ostream&
258 
259   // Note, the same problem is more apparent without protect
260   //   (std::cout << 1, std::cout << constant(2))(); // does not work
261 
262     (boost::ref(std::cout << 1), std::cout << constant(2))(); // this does
263 
264 #endif
265 
266 }
267 
268 
test_lambda_functors_as_arguments_to_lambda_functors()269 void test_lambda_functors_as_arguments_to_lambda_functors() {
270 
271 // lambda functor is a function object, and can therefore be used
272 // as an argument to another lambda functors function call object.
273 
274   // Note however, that the argument/type substitution is not entered again.
275   // This means, that something like this will not work:
276 
277     (_1 + _2)(_1, make_const(7));
278     (_1 + _2)(bind(&sum_0), make_const(7));
279 
280     // or it does work, but the effect is not to call
281     // sum_0() + 7, but rather
282     // bind(sum_0) + 7, which results in another lambda functor
283     // (lambda functor + int) and can be called again
284   BOOST_CHECK((_1 + _2)(bind(&sum_0), make_const(7))() == 7);
285 
286   int i = 3, j = 12;
287   BOOST_CHECK((_1 - _2)(_2, _1)(i, j) == j - i);
288 
289   // also, note that lambda functor are no special case for bind if received
290   // as a parameter. In oder to be bindable, the functor must
291   // defint the sig template, or then
292   // the return type must be defined within the bind call. Lambda functors
293   // do define the sig template, so if the return type deduction system
294   // covers the case, there is no need to specify the return type
295   // explicitly.
296 
297   int a = 5, b = 6;
298 
299   // Let type deduction find out the return type
300   BOOST_CHECK(bind(_1, _2, _3)(unlambda(_1 + _2), a, b) == 11);
301 
302   //specify it yourself:
303   BOOST_CHECK(bind(_1, _2, _3)(ret<int>(_1 + _2), a, b) == 11);
304   BOOST_CHECK(ret<int>(bind(_1, _2, _3))(_1 + _2, a, b) == 11);
305   BOOST_CHECK(bind<int>(_1, _2, _3)(_1 + _2, a, b) == 11);
306 
307   bind(_1,1.0)(_1+_1);
308   return;
309 
310 }
311 
312 
test_const_parameters()313 void test_const_parameters() {
314 
315   //  (_1 + _2)(1, 2); // this would fail,
316 
317   // Either make arguments const:
318   BOOST_CHECK((_1 + _2)(make_const(1), make_const(2)) == 3);
319 
320   // Or use const_parameters:
321   BOOST_CHECK(const_parameters(_1 + _2)(1, 2) == 3);
322 
323 
324 
325 }
326 
test_rvalue_arguments()327 void test_rvalue_arguments()
328 {
329   // Not quite working yet.
330   // Problems with visual 7.1
331   // BOOST_CHECK((_1 + _2)(1, 2) == 3);
332 }
333 
test_break_const()334 void test_break_const()
335 {
336 
337   // break_const is currently unnecessary, as LL supports perfect forwarding
338   // for up to there argument lambda functors, and LL does not support
339   // lambda functors with more than 3 args.
340 
341   // I'll keep the test case around anyway, if more arguments will be supported
342   // in the future.
343 
344 
345 
346   // break_const breaks constness! Be careful!
347   // You need this only if you need to have side effects on some argument(s)
348   // and some arguments are non-const rvalues and your lambda functors
349   // take more than 3 arguments.
350 
351 
352   int i = 1;
353   //  OLD COMMENT: (_1 += _2)(i, 2) // fails, 2 is a non-const rvalue
354   //  OLD COMMENT:  const_parameters(_1 += _2)(i, 2) // fails, side-effect to i
355   break_const(_1 += _2)(i, 2); // ok
356   BOOST_CHECK(i == 3);
357 }
358 
359 template<class T>
360 struct func {
361   template<class Args>
362   struct sig {
363     typedef typename boost::tuples::element<1, Args>::type arg1;
364     // If the argument type is not the same as the expected type,
365     // return void, which will cause an error.  Note that we
366     // can't just assert that the types are the same, because
367     // both const and non-const versions can be instantiated
368     // even though only one is ultimately used.
369     typedef typename boost::mpl::if_<boost::is_same<arg1, T>,
370       typename boost::remove_const<arg1>::type,
371       void
372     >::type type;
373   };
374   template<class U>
operator ()func375   U operator()(const U& arg) const {
376     return arg;
377   }
378 };
379 
test_sig()380 void test_sig()
381 {
382   int i = 1;
383   BOOST_CHECK(bind(func<int>(), 1)() == 1);
384   BOOST_CHECK(bind(func<const int>(), _1)(static_cast<const int&>(i)) == 1);
385   BOOST_CHECK(bind(func<int>(), _1)(i) == 1);
386 }
387 
388 class base {
389 public:
390   virtual int foo() = 0;
391 };
392 
393 class derived : public base {
394 public:
foo()395   virtual int foo() {
396     return 1;
397   }
398 };
399 
test_abstract()400 void test_abstract()
401 {
402   derived d;
403   base& b = d;
404   BOOST_CHECK(bind(&base::foo, var(b))() == 1);
405   BOOST_CHECK(bind(&base::foo, *_1)(&b) == 1);
406 }
407 
test_main(int,char * [])408 int test_main(int, char *[]) {
409 
410   test_nested_binds();
411   test_unlambda();
412   test_protect();
413   test_lambda_functors_as_arguments_to_lambda_functors();
414   test_const_parameters();
415   test_rvalue_arguments();
416   test_break_const();
417   test_sig();
418   test_abstract();
419   return 0;
420 }
421