template typename BinaryFunc> void testBinary(Contain1& src1, Contain1& src2, Contain2& dest, BinaryFunc f) { transform(src1.begin(), src1.end(), src2.begin(), dest.begin(), f); } // Executes the expression, then stringizes the // expression into the print statement: #define T(EXPR) EXPR; print(r.begin(), r.end(), \ "After " #EXPR); // For Boolean tests: #define B(EXPR) EXPR; print(br.begin(), br.end(), \ "After " #EXPR); // Boolean random generator: struct BRand { BRand() { srand(time(0)); } bool operator()() { return rand() > RAND_MAX / 2; } }; int main() { const int sz = 10; const int max = 50; vector // An integer random number generator: URandGen urg(max); generate_n(x.begin(), sz, urg); generate_n(y.begin(), sz, urg); // Add one to each to guarantee nonzero divide: transform(y.begin(), y.end(), y.begin(), bind2nd(plus // Guarantee one pair of elements is ==: x[0] = y[0]; print(x.begin(), x.end(), "x"); print(y.begin(), y.end(), "y"); // Operate on each element pair of x & y, // putting the result into r: T(testBinary(x, y, r, plus T(testBinary(x, y, r, minus T(testBinary(x, y, r, multiplies T(testBinary(x, y, r, divides T(testBinary(x, y, r, modulus T(testUnary(x, r, negate vector B(testBinary(x, y, br, equal_to B(testBinary(x, y, br, not_equal_to B(testBinary(x, y, br, greater B(testBinary(x, y, br, less B(testBinary(x, y, br, greater_equal B(testBinary(x, y, br, less_equal B(testBinary(x, y, br, not2(greater_equal B(testBinary(x,y,br,not2(less_equal vector generate_n(b1.begin(), sz, BRand()); generate_n(b2.begin(), sz, BRand()); print(b1.begin(), b1.end(), "b1"); print(b2.begin(), b2.end(), "b2"); B(testBinary(b1, b2, br, logical_and B(testBinary(b1, b2, br, logical_or B(testUnary(b1, br, logical_not B(testUnary(b1, br, not1(logical_not } ///:~ To keep this example short, we used a few handy tricks. The print( ) template is designed to print any sequence, along with an optional message. Since print( ) uses the copy( ) algorithm to send objects to cout via an ostream_iterator, the ostream_iterator must know the type of object it is printing, which we infer from the value_type member of the iterator passed.[83] As you can see in main( ), however, the compiler can deduce the type of T when you hand it a vector The next two template functions automate the process of testing the various function object templates. There are two since the function objects are either unary or binary. The testUnary( ) function takes a source vector, a destination vector, and a unary function object to apply to the source vector to produce the destination vector. In testBinary( ), two source vectors are fed to a binary function to produce the destination vector. In both cases, the template functions simply turn around and call the transform( ) algorithm, which applies the unary function/function object found in its fourth parameter to each sequence element, writing the result to the sequence indicated by its third parameter, which in this case is the same as the input sequence. For each test, you want to see a string describing the test, followed by the results of the test. To automate this, the preprocessor comes in handy; the T( ) and B( ) macros each take the expression you want to execute. After evaluating the expression, they pass the appropriate range to print( ). To produce the message the expression is "string-ized" using the preprocessor. That way you see the code of the expression that is executed followed by the result vector. The last little tool, BRand, is a generator object that creates random bool values. To do this, it gets a random number from rand( ) and tests to see if it’s greater than (RAND_MAX+1)/2. If the random numbers are evenly distributed, this should happen half the time. In main( ), three vectors of int are created: x and y for source values, and r for results. To initialize x and y with random values no greater than 50, a generator of type URandGen from Generators.h is used. The standard generate_n( ) algorithm populates the sequence specified in its first argument by invoking its third argument (which must be a generator) a given number of times (specified in its second argument). Since there is one operation in which elements of x are divided by elements of y, we must ensure that there are no zero values of y. This is accomplished by once again using the transform( ) algorithm, taking the source values from y and putting the results back into y. The function object for this is created with the expression:. bind2nd(plus This expression uses the plus function object to add 1 to its first argument. As we did earlier in this chapter, we use a binder adapter to make this a unary function so it can applied to the sequence by a single call to transform( ). Another test in the program compares the elements in the two vectors for equality, so it is interesting to guarantee that at least one pair of elements is equivalent; in this case element zero is chosen. Once the two vectors is printed, T( ) tests each of the function objects that produces a numeric value, and then B( ) tests each function object that produces a Boolean result. The result is placed into a vector x: 4 8 18 36 22 6 29 19 25 47 y: 4 14 23 9 11 32 13 15 44 30 After testBinary(x, y, r, plus 8 22 41 45 33 38 42 34 69 77 After testBinary(x, y, r, minus 0 -6 -5 27 11 -26 16 4 -19 17 After testBinary(x, y, r, multiplies 16 112 414 324 242 192 377 285 1100 1410 After testBinary(x, y, r, divides 1 0 0 4 2 0 2 1 0 1 After testBinary(x, y, r, limit 0 8 18 0 0 6 3 4 25 17 After testUnary(x, r, negate -4 -8 -18 -36 -22 -6 -29 -19 -25 -47 After testBinary(x, y, br, equal_to 1 0 0 0 0 0 0 0 0 0 After testBinary(x, y, br, not_equal_to 0 1 1 1 1 1 1 1 1 1 After testBinary(x, y, br, greater 0 0 0 1 1 0 1 1 0 1 After testBinary(x, y, br, less 0 1 1 0 0 1 0 0 1 0 After testBinary(x, y, br, greater_equal 1 0 0 1 1 0 1 1 0 1 After testBinary(x, y, br, less_equal 1 1 1 0 0 1 0 0 1 0 After testBinary(x, y, br, not2(greater_equal 0 1 1 0 0 1 0 0 1 0 After testBinary(x,y,br,not2(less_equal 0 0 0 1 1 0 1 1 0 1 b1: 0 1 1 0 0 0 1 0 1 1 b2: 0 1 1 0 0 0 1 0 1 1 After testBinary(b1, b2, br, logical_and 0 1 1 0 0 0 1 0 1 1 After testBinary(b1, b2, br, logical_or 0 1 1 0 0 0 1 0 1 1 After testUnary(b1, br, logical_not 1 0 0 1 1 1 0 1 0 0 After testUnary(b1, br, not1(logical_not 0 1 1 0 0 0 1 0 1 1 A binder doesn’t have to produce a unary //: C06:FBinder.cpp // Binders aren't limited to producing predicates #include #include #include #include #include #include "Generators.h" using namespace std; int main() { ostream_iterator vector generate(v.begin(), v.end(), URandGen(20)); copy(v.begin(), v.end(), out); transform(v.begin(), v.end(), v.begin(), bind2nd(multiplies copy(v.begin(), v.end(), out); } ///:~ Since the third argument to transform( ) is the same as the first, the resulting elements are copied back into the source vector. The function object created by bind2nd( ) in this case produces an int result. The "bound" argument to a binder cannot be a function object, but it does not have to be a compile-time constant. For example:. //: C06:BinderValue.cpp // The bound argument can vary #include #include #include #include using namespace std; int boundedRand() { return rand() % 100; } int main() { const int sz = 20; int a[sz], b[sz] = {0}; generate(a, a + sz, boundedRand); int val = boundedRand(); int* end = remove_copy_if(a, a + sz, b, bind2nd(greater // Sort for easier viewing: sort(a, a + sz); sort(b, end); ostream_iterator cout << "Original Sequence:\n"; copy(a, a + sz, out); cout << endl; cout << "Values less <= " << val << endl; copy(b, end, out); cout << endl; } ///:~ Here, an array is filled with 20 random numbers between 0 and 100, and the user provides a value on the command line. In the remove_copy_if( ) call, you can see that the bound argument to bind2nd( ) is random number in the same range as the sequence. The output of a sample execution follows. Original Sequence: 4 12 15 17 19 21 26 30 47 48 56 58 60 63 71 79 82 90 92 95 Values less <= 41 4 12 15 17 19 21 26 30