On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com

How To Guides
Virtualization
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions

Thinking in C++ Vol 2 - Practical Programming
Prev Home 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 member 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:

//: C05:ApplySequence.h
// Apply a function to an STL sequence container.

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

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

// const, 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) const,
A1 a1, A2 a2) {
typename Seq::iterator it = sq.begin();
while(it != sq.end())
((*it++)->*f)(a1, a2);
}

// Non-const, 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)();
}

// Non-const, 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);
}

// Non-const, 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);
}
// Etc., to handle maximum likely arguments ///:~

The apply( ) function template above 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 sequence and apply the function to every object. We have overloaded on the const-ness of the function so you can use it with both const and non-const functions.

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

You can see there is more than one version of apply( ), further illustrating overloading of function templates. Although these templates allow 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; you must decide how many arguments will ever be required and make the appropriate definitions.

To test the various overloaded versions of apply( ), the class Gromit[59] is created containing functions with different numbers of arguments, and both const and non-const member functions:

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

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

Now you can use the apply( ) template functions to apply the Gromit member functions to a vector<Gromit*>, like this:

//: C05:ApplyGromit.cpp
// Test ApplySequence.h.
#include <cstddef>
#include <iostream>
#include <vector>
#include "ApplySequence.h"
#include "Gromit.h"
#include "../purge.h"
using namespace std;

int main() {
vector<Gromit*> dogs;
for(size_t 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);
purge(dogs);
} ///:~

The purge( ) function is a small utility that calls delete on every element of sequence. You ll find it defined in Chapter 7, and used various places in this book.

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 use it knowing only what it is intended to accomplish, not how. This is the type of division you should strive for in all your program components. The tough details are all isolated on the designer s side of the wall. Users are concerned only with accomplishing their goals and don t see, know about, or depend on details of the underlying implementation. We ll explore even more flexible ways to apply functions to sequences in the next chapter.

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

 Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire