Bruce Eckel's Thinking in C++, 2nd Ed Contents | Prev | Next

Applying a function to an STL sequence

Suppose you want to take an STL sequence container (which you’ll learn more about in subsequent chapters; for now we can just use the familiar vector) and apply a function to all the objects it contains. Because a vector can contain any type of object, you need a function that works with any type of vector and any type of object it contains:

//: C19:applySequence.h
// Apply a function to an STL sequence container

// 0 arguments, any type of return value:
template<class Seq, class T, class R>
void apply(Seq& sq, R (T::*f)()) {
  typename Seq::iterator it = sq.begin();
  while(it != sq.end()) {
    ((*it)->*f)();
    it++;
  }
}

// 1 argument, any type of return value:
template<class Seq, class T, class R, class A>
void apply(Seq& sq, R(T::*f)(A), A a) {
  typename Seq::iterator it = sq.begin();
  while(it != sq.end()) {
    ((*it)->*f)(a);
    it++;
  }
}

// 2 arguments, any type of return value:
template<class Seq, class T, class R, 
         class A1, class A2>
void apply(Seq& sq, R(T::*f)(A1, A2),
    A1 a1, A2 a2) {
  typename Seq::iterator it = sq.begin();
  while(it != sq.end()) {
    ((*it)->*f)(a1, a2);
    it++;
  }
}
// Etc., to handle maximum likely arguments ///:~

The apply( ) function template takes a reference to the container class and a pointer-to-member for a member function of the objects contained in the class. It uses an iterator to move through the Stack and apply the function to every object. If you’ve (understandably) forgotten the pointer-to-member syntax, you can refresh your memory at the end of Chapter XX.

Notice that there are no STL header files (or any header files, for that matter) included in applySequence.h, so it is actually not limited to use with an STL sequence. However, it does make assumptions (primarily, the name and behavior of the iterator) that apply to STL sequences.

You can see there is more than one version of apply( ), so it’s possible to overload function templates. Although they all take any type of return value (which is ignored, but the type information is required to match the pointer-to-member), each version takes a different number of arguments, and because it’s a template, those arguments can be of any type. The only limitation here is that there’s no “super template” to create templates for you; thus you must decide how many arguments will ever be required.

To test the various overloaded versions of apply( ), the class Gromit[58] is created containing functions with different numbers of arguments:

//: C19:Gromit.h
// The techno-dog. Has member functions 
// with various numbers of arguments.
#include <iostream>

class Gromit { 
  int arf;
public:
  Gromit(int arf = 1) : arf(arf + 1) {}
  void speak(int) {
    for(int i = 0; i < arf; i++)
      std::cout << "arf! ";
    std::cout << std::endl;
  }
  char eat(float) {
    std::cout << "chomp!" << std::endl;
    return 'z';
  }
  int sleep(char, double) {
    std::cout << "zzz..." << std::endl;
    return 0;
  }
  void sit(void) {}
}; ///:~

Now the apply( ) template functions can be combined with a vector<Gromit*> to make a container that will call member functions of the contained objects, like this:

//: C19:applyGromit.cpp
// Test applySequence.h
#include "Gromit.h"
#include "applySequence.h"
#include <vector>
#include <iostream>
using namespace std;

int main() {
  vector<Gromit*> dogs;
  for(int i = 0; i < 5; i++)
    dogs.push_back(new Gromit(i));
  apply(dogs, &Gromit::speak, 1);
  apply(dogs, &Gromit::eat, 2.0f);
  apply(dogs, &Gromit::sleep, 'z', 3.0);
  apply(dogs, &Gromit::sit);
} ///:~

Although the definition of apply( ) is somewhat complex and not something you’d ever expect a novice to understand, its use is remarkably clean and simple, and a novice could easily use it knowing only what it is intended to accomplish, not how. This is the type of division you should strive for in all of your program components: The tough details are all isolated on the designer’s side of the wall, and users are concerned only with accomplishing their goals, and don’t see, know about, or depend on details of the underlying implementation


[58] A reference to the British animated short The Wrong Trousers by Nick Park.

Contents | Prev | Next


Contact: webmaster@codeguru.com
CodeGuru - the website for developers.
[an error occurred while processing this directive]