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