• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if defined(BOOST_NO_CXX11_HDR_FUNCTIONAL)
125 
126   BOOST_CHECK(call_with_100(bl::bind(std_functor(std::bind1st(std::plus<int>(), 1)), _1)) == 101);
127 
128 #else
129 
130   BOOST_CHECK(call_with_100(bl::bind(std_functor(std::bind(std::plus<int>(), 1, std::placeholders::_1)), _1)) == 101);
131 
132 #endif
133 
134   // std_functor insturcts LL that the functor defines a result_type typedef
135   // rather than a sig template.
136   bl::bind(std_functor(std::plus<int>()), _1, _2)(i, i);
137 }
138 
139 
140 
141 
142 // protect ------------------------------------------------------------
143 
144 // protect protects a lambda functor from argument substitution.
145 // protect is useful e.g. with nested stl algorithm calls.
146 
147 namespace ll {
148 
149 struct for_each {
150 
151   // note, std::for_each returns it's last argument
152   // We want the same behaviour from our ll::for_each.
153   // However, the functor can be called with any arguments, and
154   // the return type thus depends on the argument types.
155 
156   // 1. Provide a sig class member template:
157 
158   // The return type deduction system instantiate this class as:
159   // sig<Args>::type, where Args is a boost::tuples::cons-list
160   // The head type is the function object type itself
161   // cv-qualified (so it is possilbe to provide different return types
162   // for differently cv-qualified operator()'s.
163 
164   // The tail type is the list of the types of the actual arguments the
165   // function was called with.
166   // So sig should contain a typedef type, which defines a mapping from
167   // the operator() arguments to its return type.
168   // Note, that it is possible to provide different sigs for the same functor
169   // if the functor has several operator()'s, even if they have different
170   // number of arguments.
171 
172   // Note, that the argument types in Args are guaranteed to be non-reference
173   // types, but they can have cv-qualifiers.
174 
175     template <class Args>
176   struct sig {
177     typedef typename boost::remove_const<
178           typename boost::tuples::element<3, Args>::type
179        >::type type;
180   };
181 
182   template <class A, class B, class C>
183   C
operator ()ll::for_each184   operator()(const A& a, const B& b, const C& c) const
185   { return std::for_each(a, b, c);}
186 };
187 
188 } // end of ll namespace
189 
test_protect()190 void test_protect()
191 {
192   int i = 0;
193   int b[3][5];
194   int* a[3];
195 
196   for(int j=0; j<3; ++j) a[j] = b[j];
197 
198   std::for_each(a, a+3,
199            bind(ll::for_each(), _1, _1 + 5, protect(_1 = ++var(i))));
200 
201   // This is how you could output the values (it is uncommented, no output
202   // from a regression test file):
203   //  std::for_each(a, a+3,
204   //                bind(ll::for_each(), _1, _1 + 5,
205   //                     std::cout << constant("\nLine ") << (&_1 - a) << " : "
206   //                     << protect(_1)
207   //                     )
208   //               );
209 
210   int sum = 0;
211 
212   std::for_each(a, a+3,
213            bind(ll::for_each(), _1, _1 + 5,
214                 protect(sum += _1))
215                );
216   BOOST_CHECK(sum == (1+15)*15/2);
217 
218   sum = 0;
219 
220   std::for_each(a, a+3,
221            bind(ll::for_each(), _1, _1 + 5,
222                 sum += 1 + protect(_1)) // add element count
223                );
224   BOOST_CHECK(sum == (1+15)*15/2 + 15);
225 
226   (1 + protect(_1))(sum);
227 
228   int k = 0;
229   ((k += constant(1)) += protect(constant(2)))();
230   BOOST_CHECK(k==1);
231 
232   k = 0;
233   ((k += constant(1)) += protect(constant(2)))()();
234   BOOST_CHECK(k==3);
235 
236   // note, the following doesn't work:
237 
238   //  ((var(k) = constant(1)) = protect(constant(2)))();
239 
240   // (var(k) = constant(1))() returns int& and thus the
241   // second assignment fails.
242 
243   // We should have something like:
244   // bind(var, var(k) = constant(1)) = protect(constant(2)))();
245   // But currently var is not bindable.
246 
247   // The same goes with ret. A bindable ret could be handy sometimes as well
248   // (protect(std::cout << _1), std::cout << _1)(i)(j); does not work
249   // because the comma operator tries to store the result of the evaluation
250   // of std::cout << _1 as a copy (and you can't copy std::ostream).
251   // something like this:
252   // (protect(std::cout << _1), bind(ref, std::cout << _1))(i)(j);
253 
254 
255   // the stuff below works, but we do not want extra output to
256   // cout, must be changed to stringstreams but stringstreams do not
257   // work due to a bug in the type deduction. Will be fixed...
258 #if 0
259   // But for now, ref is not bindable. There are other ways around this:
260 
261     int x = 1, y = 2;
262     (protect(std::cout << _1), (std::cout << _1, 0))(x)(y);
263 
264   // added one dummy value to make the argument to comma an int
265   // instead of ostream&
266 
267   // Note, the same problem is more apparent without protect
268   //   (std::cout << 1, std::cout << constant(2))(); // does not work
269 
270     (boost::ref(std::cout << 1), std::cout << constant(2))(); // this does
271 
272 #endif
273 
274 }
275 
276 
test_lambda_functors_as_arguments_to_lambda_functors()277 void test_lambda_functors_as_arguments_to_lambda_functors() {
278 
279 // lambda functor is a function object, and can therefore be used
280 // as an argument to another lambda functors function call object.
281 
282   // Note however, that the argument/type substitution is not entered again.
283   // This means, that something like this will not work:
284 
285     (_1 + _2)(_1, make_const(7));
286     (_1 + _2)(bind(&sum_0), make_const(7));
287 
288     // or it does work, but the effect is not to call
289     // sum_0() + 7, but rather
290     // bind(sum_0) + 7, which results in another lambda functor
291     // (lambda functor + int) and can be called again
292   BOOST_CHECK((_1 + _2)(bind(&sum_0), make_const(7))() == 7);
293 
294   int i = 3, j = 12;
295   BOOST_CHECK((_1 - _2)(_2, _1)(i, j) == j - i);
296 
297   // also, note that lambda functor are no special case for bind if received
298   // as a parameter. In oder to be bindable, the functor must
299   // defint the sig template, or then
300   // the return type must be defined within the bind call. Lambda functors
301   // do define the sig template, so if the return type deduction system
302   // covers the case, there is no need to specify the return type
303   // explicitly.
304 
305   int a = 5, b = 6;
306 
307   // Let type deduction find out the return type
308   BOOST_CHECK(bind(_1, _2, _3)(unlambda(_1 + _2), a, b) == 11);
309 
310   //specify it yourself:
311   BOOST_CHECK(bind(_1, _2, _3)(ret<int>(_1 + _2), a, b) == 11);
312   BOOST_CHECK(ret<int>(bind(_1, _2, _3))(_1 + _2, a, b) == 11);
313   BOOST_CHECK(bind<int>(_1, _2, _3)(_1 + _2, a, b) == 11);
314 
315   bind(_1,1.0)(_1+_1);
316   return;
317 
318 }
319 
320 
test_const_parameters()321 void test_const_parameters() {
322 
323   //  (_1 + _2)(1, 2); // this would fail,
324 
325   // Either make arguments const:
326   BOOST_CHECK((_1 + _2)(make_const(1), make_const(2)) == 3);
327 
328   // Or use const_parameters:
329   BOOST_CHECK(const_parameters(_1 + _2)(1, 2) == 3);
330 
331 
332 
333 }
334 
test_rvalue_arguments()335 void test_rvalue_arguments()
336 {
337   // Not quite working yet.
338   // Problems with visual 7.1
339   // BOOST_CHECK((_1 + _2)(1, 2) == 3);
340 }
341 
test_break_const()342 void test_break_const()
343 {
344 
345   // break_const is currently unnecessary, as LL supports perfect forwarding
346   // for up to there argument lambda functors, and LL does not support
347   // lambda functors with more than 3 args.
348 
349   // I'll keep the test case around anyway, if more arguments will be supported
350   // in the future.
351 
352 
353 
354   // break_const breaks constness! Be careful!
355   // You need this only if you need to have side effects on some argument(s)
356   // and some arguments are non-const rvalues and your lambda functors
357   // take more than 3 arguments.
358 
359 
360   int i = 1;
361   //  OLD COMMENT: (_1 += _2)(i, 2) // fails, 2 is a non-const rvalue
362   //  OLD COMMENT:  const_parameters(_1 += _2)(i, 2) // fails, side-effect to i
363   break_const(_1 += _2)(i, 2); // ok
364   BOOST_CHECK(i == 3);
365 }
366 
367 template<class T>
368 struct func {
369   template<class Args>
370   struct sig {
371     typedef typename boost::tuples::element<1, Args>::type arg1;
372     // If the argument type is not the same as the expected type,
373     // return void, which will cause an error.  Note that we
374     // can't just assert that the types are the same, because
375     // both const and non-const versions can be instantiated
376     // even though only one is ultimately used.
377     typedef typename boost::mpl::if_<boost::is_same<arg1, T>,
378       typename boost::remove_const<arg1>::type,
379       void
380     >::type type;
381   };
382   template<class U>
operator ()func383   U operator()(const U& arg) const {
384     return arg;
385   }
386 };
387 
test_sig()388 void test_sig()
389 {
390   int i = 1;
391   BOOST_CHECK(bind(func<int>(), 1)() == 1);
392   BOOST_CHECK(bind(func<const int>(), _1)(static_cast<const int&>(i)) == 1);
393   BOOST_CHECK(bind(func<int>(), _1)(i) == 1);
394 }
395 
396 class base {
397 public:
398   virtual int foo() = 0;
399 };
400 
401 class derived : public base {
402 public:
foo()403   virtual int foo() {
404     return 1;
405   }
406 };
407 
test_abstract()408 void test_abstract()
409 {
410   derived d;
411   base& b = d;
412   BOOST_CHECK(bind(&base::foo, var(b))() == 1);
413   BOOST_CHECK(bind(&base::foo, *_1)(&b) == 1);
414 }
415 
test_main(int,char * [])416 int test_main(int, char *[]) {
417 
418   test_nested_binds();
419   test_unlambda();
420   test_protect();
421   test_lambda_functors_as_arguments_to_lambda_functors();
422   test_const_parameters();
423   test_rvalue_arguments();
424   test_break_const();
425   test_sig();
426   test_abstract();
427   return 0;
428 }
429