NOTE:

These are a little sketchy...I'll fill in details 'soon'.

The C Way

Function Pointers:

Function PrototypeFunction Pointer Type
char * strcpy(char * dest, const char * src); char * (*)(char *,const char *)
int toupper(int ch); int (*)(int)
time_t time(time_t * result_now); time_t (*)(time_t *)
void srand(unsigned int seed); void (*)(unsigned int)
int rand(void); int (*)(void)

The C++ Way

Use a template argument to a function:

    template <typename ArrT, typename FuncT>
    void foreach(ArrT & arr, long len, FuncT call_me)
    {
        for (long c = 0; c != len; c++)
        {
            call_me(arr[c]);
        }
        return;
    }

The foreach template accepts an array of arbitrary type and a function. It then calls the function passing it subsequent elements of the array. Let's look at calling and instantiating this template. First, the instantiation list:

    ArrT:  operator[](long) --> T
    FuncT:  copy constructor
            operator()(T)

Now a few attempts to call and their results:

    void print_nice(double x)
    {
        ios_base::fmtflags rememberF = cout.setf(ios_base::right|
                                                 ios_base::showpoint|
                                                 ios_base::fixed);
        streamsize rememberP = cout.precision(3);
        cout << setw(7) << x;
        cout.setf(rememberF);
        cout.precision(rememberP);
        return;
    }

    double data[MAX_D];
    short num_data;
    // fill data and count in num_data
    foreach(data, num_data, print_nice);

First check the instantiation list:

    ArrT as double*:  op[](long)    check returns double (for T)
    FuncT as void(*)(double):  copy ctor     check (pointers can be copied)
                               op()(double)  check (function pointers have
                                                    op() and this one takes
                                                    a single double)

So what happens? Let's say that data contains { 3.1897, 0.0004, 2.9991 } and that (therefore) num_data is 3. We'd see on the screen:

   3.190  0.000  2.999_

Note how each number is rounded on-screen to 3 decimal places and is right-justified in a field 7 characters wide.

That wasn't so bad, let's try again:

    class Total
    {
        double s;
    public:
        Total(double x = 0.0) : s(x) {}
        double operator()(double x) { return s += x; }
        double operator()(void) const { return s; }
        double reset(double x = 0.0) { swap(x,s); return x; }
    };

    long a[MAX];
    short count;
    Total sum;
    // fill a and accumulate count
    foreach(a, count, sum);
    cout << "Total is " << sum() << ".\n";

First, instantiation:

    ArrT as long*:  op[](long)    check returns long (for T)
    FuncT as Total:  copy ctor    check (built-in provided)
                                  op()(long)  check (double is compatible)

Let's assume a contains { 10, 14, 6, 2, 8 } and that count is 5. Executing we see:

Total is 0.
_

How odd. I'd've expected more like 40...weird. What's happening?

Note that the FuncT argument is copy constructed. This was fine for the function pointers. But now that we have a function object, copy construction is working against us. We are accumulating a sum in the formal argument — which is destroyed before the foreach function returns! Our variable sum is unaffected the whole time...

The solution, obviously, is to pass the FuncT by reference:

    template <typename ArrT, typename FuncT>
    void foreach(ArrT & arr, long len, FuncT & call_me)
    {
        for (long c = 0; c != len; c++)
        {
            call_me(arr[c]);
        }
        return;
    }

Now we get 40 as we thought we would. Older compilers, however, won't like the function pointer being passed as reference — we shouldn't be changing the address of a function, now should we? For those compilers, you'll have to make several changes:

Unfortunately, newer compilers might complain about the constant reference to a function pointer and *not* about the regular reference...*sigh* You'll just have to keep both ideas in mind and use the one your compiler requires.

(Note: making the s member mutable goes against our earlier rules for use of mutable. We originally said that mutable should only be applied to maintenance data members. i.e. Those members not storing the data which was the primary reason for creating the class in the first place. Members, instead, which are there to enhance the class user's experience by providing capabilities or speed. On those old compilers, though, this use of mutable is a necessary kludge to get both function pointers and function objects to pass through a function argument to a template.)

Lecture Notes

Here are some notes from lecture to round out this discussion.