NOTE:

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

Flags and You

Stream Formatting

Output Features

Width and Characters

This should work, but this is sometimes neglected by compiler/library writers. Since we can't depend on setting the width on individual char values, we can resort to something like this:

   << setw(desired_width-1) << "" << character

Note how we set a width one less on a Cstring literal (the empty string) and then print the character. If you wanted it to be left-justified instead, just do this:

   << character << setw(desired_width-1) << ""

Of course, if you are printing a literal character, you could just print it inside a string:

   << setw(desired_width) << "*"

Would left-justify a * in desired_width spaces.

Centering

Centering is not provided directly by the stream library. However, we typically do something like this:

   << setw((field_width+item_width)/2) << item 

(note the ON-PURPOSE integer division!!).

This simplistic version, however, doesn't take other factors into account:

We can alleviate such concerns via:

    << setw((field_width-item_width)/2) << "" << item

And now all that's really missing is the case where this field is not the last column of a table? We need to fill in after the data like so:

    << setw((field_width-item_width)/2) << "" << item
    << setw(field_width-item_width-(field_width-item_width)/2) << ""

This will pad afterwards with just the missing spacing.

The main thing missing here is: "How do I know the item_width?" For a char or Cstring, it is pretty simple: 1 or strlen(). But for floating point numbers, you'll have to calculate the number of digits in the number before the decimal point, add one for the decimal point, then add the stream's current precision:

    item_width = numdigit(item)+1+os.precision();

(The overload of precision returns the current precision setting of the stream.) For a whole number, simply get the number of digits in the number. Also don't forget to include space for a sign if you are expecting negatives (or if the stream has showpos turned on).

Playing Well with Others

When you are coding a single function or method, it is a good idea to not assume things about the state of a stream either before you started using it or after you are done with it. Try to remember the original values and reset them before you leave:

    ... function(ostream & os)
    {
        // remember old settings -- returned by methods -- as we
        // set our desired values
        char old_fill = os.fill('-');
        ios_base::fmtflags old_flags = os.setf(ios_base::left|ios_base::showbase);
        streamsize old_prec = os.precision(4);

        // do things to stream as needed

        // replace original settings to wipe out any changes we've
        // made
        os.fill(old_fill);
        os.flags(old_flags);  // setf would just | the flags together...leaving ours
        os.precision(old_prec);

        return ...;
    }

The width need not be restored since it lasts for only a single output item, anyway.

(Technically the char for the fill character should be basic_ios::char_type, but unless you are programming internationally, it will end up being whatever char is.) (You did know that char changes from standard ASCII to Extended ASCII to Unicode on various platforms, didn't you? *smile*)

Width and Your ADT

If you are writing output/display methods/functions for an ADT, you can attempt to format as desired by retrieving the stream's current settings and using those to format your overall result. This can be done in many ways. Here's a simplistic example:

    void Rational::output(ostream & os) const
    {
        streamsize cur_width = os.width();
        if (cur_width != 0)    // this assumes right justification!!!
        {
            short den_len = numdigits(denom);
            os << setw(cur_width-den_len-1) << numer << '/'
               << denom;
        }
        else  // default is as much width as needed
        {
            os << numer << '/' << denom;
        }
        return;
    }

But what if the stream is set to left-justify numbers?! We can use some flag manipulation techniques to determine that:

    void Rational::output(ostream & os) const
    {
        streamsize cur_width = os.width();
        if (cur_width != 0)
        {
            short len;
            if ((os.flags() & ios_base::adjustfield) == ios_base::left)
            {
                len = numdigits(numer);
                os << numer << '/' << setw(cur_width-len-1)
                   << denom;
            }
            else
            {
                len = numdigits(denom);
                os << setw(cur_width-len-1) << numer << '/'
                   << denom;
            }
        }
        else  // default is as much width as needed
        {
            os << numer << '/' << denom;
        }
        return;
    }

Here the flags() method returns all the flags at once. We use the bit-wise and to extract the bits for the adjustment flags only (leaving all others 0's). If this is set to left-justification, we print the numerator and slash and justify the denominator. If it is not, we perform as before (justify the numerator and then print the slash and denominator afterwards).

The only question left is what happens if you set a width less than 0? (Perhaps you should try it out...)

Input Features