Выбрать главу

const int CharGen::len = strlen(source);

#endif // GENERATORS_H ///:~

We’ll be using these generating functions in various examples throughout this chapter. The SkipGen function object returns the next number of an arithmetic sequence whose common difference is held in its skp data member. A URandGen object generates a unique random number in a specified range. (It uses a set container, which we’ll discuss in the next chapter.) A CharGen object returns a random alphabetic character. Here is the sample program we promised, which uses URandGen.

//: C06:FunctionObjects.cpp

//{-bor}

// Illustrates selected predefined function object

// templates from the standard C++ library

#include <algorithm>

#include <functional>

#include <iostream>

#include <iterator>

#include <vector>

#include "Generators.h"

using namespace std;

template<class Iter>

void print(Iter b, Iter e, char* msg = "") {

  if(msg != 0 && *msg != 0)

    cout << msg << ":" << endl;

  typedef typename Iter::value_type T;

  copy(b, e, ostream_iterator<T>(cout, " "));

  cout << endl;

}

template<typename Contain, typename UnaryFunc>

void testUnary(Contain& source, Contain& dest,

  UnaryFunc f) {

  transform(source.begin(), source.end(),

    dest.begin(), f);

}

template<typename Contain1, typename Contain2,

  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<int> x(sz), y(sz), r(sz);

  // 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<int>(), 1));

  // 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<int>()));

  T(testBinary(x, y, r, minus<int>()));

  T(testBinary(x, y, r, multiplies<int>()));

  T(testBinary(x, y, r, divides<int>()));

  T(testBinary(x, y, r, modulus<int>()));

  T(testUnary(x, r, negate<int>()));

  vector<bool> br(sz); // For Boolean results

  B(testBinary(x, y, br, equal_to<int>()));

  B(testBinary(x, y, br, not_equal_to<int>()));

  B(testBinary(x, y, br, greater<int>()));

  B(testBinary(x, y, br, less<int>()));

  B(testBinary(x, y, br, greater_equal<int>()));

  B(testBinary(x, y, br, less_equal<int>()));

  B(testBinary(x, y, br,

    not2(greater_equal<int>())));

  B(testBinary(x,y,br,not2(less_equal<int>())));

  vector<bool> b1(sz), b2(sz);

  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<int>()));

  B(testBinary(b1, b2, br, logical_or<int>()));

  B(testUnary(b1, br, logical_not<int>()));

  B(testUnary(b1, br, not1(logical_not<int>())));

} ///:~

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<T>, so you don’t have to specify that template argument explicitly; you just say print(x) to print the vector<T> x.

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:.