Topical Information

A template idea to add to the String class.

Program Information

Having finished String insertion and String width setting (and possibly even the String formatting lab, too), you feel the next natural step would be to allow some sort of manipulator like the streams enjoy:

    String x;
    x << setw(40) << data << endl;

Of course setw and endl work on stream-type objects -- not Strings. So, let's shoot for:

    String x;
    x << Setw(40) << data << Endl;

instead. It'll be almost that easy.

The key is to look at it the way the streams do:

   cout << setw(40) << data << endl;

is actually translated into:

   cout.operator << (setw(40)).operator << (data).operator << (endl);

With each operator<< returning the stream so the next operator can use it. What are setw and endl, though? They are functions, right? Well, in truth, they are function objects. But it is fairly obvious that they are being passed to the operator function. That means that we need to overload the operator to accept a function argument.

But how can we have all those arguments passed on some and none on others? Well, to tell you the truth, the arguments aren't to the function -- they are to the constructor. The 40 above is passed to the initialization constructor of the setw class -- constructing a setw-type object to pass to the operator!

And endl? Well, it is actually a simple function -- because it needs no other arguments.

So, what does this new operator<< look like such that it can take these objects/functions? Well, it simply needs to pass the object in question (cout in that last example) to its function argument and return the object afterwards (as the other operator<<'s do). It could be something like:

    template <typename func_type>
    class & class::operator<< (func_type func)
    {
        func(*this);
        return *this;
    }

Or some reasonable approximation thereof.

(I'll let you fill in the details. The one for cout above looks something like:

    class Func
    {
        type data;
    public:
        friend Func func(type d);
        friend ostream & operator<< (ostream & os, Func & f);
    };

    ostream & Func::operator<< (ostream & os, Func & f)
    {
        os.meth(f.data);
        return os;
    }

    Func func(type d)
    {
        Func f;
        f.data = d;
        return f;
    }

There's a different one for each manipulator...kinda tricky...and definitely long-winded.

But it could have been as simple as:

    template <typename func_type>
    ostream & ostream::operator<< (func_type func)
    {
        func(*this);
        return *this;
    }

Where func accepts the stream by reference and then modifies it as is its nature -- calling the stream's own setf, precision, width, etc. as necessary.)

So now it is just left to implement a few manipulators (in strmanip.h?) and overload another operator<< for our String class. (I'll let you decide which manipulator format you understand/prefer. *grin*)

Don't forget to test your modifications (perhaps adding to the original test application?).

Thought Provoking Questions

  1. How does a function object get used as a function?

  2. How can an argument be a function during one call and an object during another?

  3. If you were to make your endl into a function object instead of a plain function, how would its use change?

  4. What manipulators did you overload? Are any of them useful for both insertion and extraction? Maybe you should overload extraction (translation) to be able to take a manipulator, too..?

This assignment is (Level 3).

Options

Add (Level 1) to overload the extraction operation to accept manipulators for the String class, too. (Setw would be useful at the very least...)