General usage

Table of contents:

Which header file to include

See the Files section for a list of all headers that can be included individually. Also see Details on Header Files for a table representation of the most commonly used subset of those header files.

Send formatted data to a stream (formatted output)

Send an instance of iof::fmtr to a stream, followed by the objects to output and format:

std::cout << iof::fmtr(format_string) << obj1 << obj2 ...;

The format_string is a character string just like that of C's printf: a mixture of ordinary text and format markers (aka placeholders, format tags, etc), one format marker per object to output.

A format marker starts with '%' and ends with either 's' or 'S' regardless of the type of the object being output (contrary to C's printf family of functions, there is no need to specify the type of object being output, as the C++ compiler knows it and doesn't make mistakes -- so it's one less thing to remember)

A format marker may contain format specification characters (FSC) in between the opening '%' and closing 's' or 'S'. These characters set the format state of the stream before the corresponding object is sent to it. The full set of output FSC is available.

Note that the FSC are always combined with the format state that the stream had when the iof::fmtr was created, so the "base format state" is always known (typically, the default adopted by the stream) for each format marker.

Note finally that if you want a '%' sign in a format string you double it (as with printf): iof::fmtr("%s%%") << percentage;

Make your class usable by iof: with operator<<(stream&)

There is no special requirement on the type of the objects following the iof::fmtr, except that they must have the usual

std::ostream& operator<<(std::ostream&, T);

defined for themselves. I.e., if you can output an instance of your class to an STL stream, then you can use it in formatted output.

Note that 'T' is the type of the object or a (usually const) reference of the object's type. All fundamental types (bool, short, int, etc) already have this. E.g.

#include "iof/io.hpp"

class Time {
    Time(int hr, int min, int sec)
        : hr(hr), min(min), sec(sec) {}
    int hr, min, sec;
    
    friend std::ostream& 
        operator<<(std::ostream&, const Time&);
}

std::ostream& operator<<(std::ostream& out, const Time& t) {
    return out << t.hr << t.min << t.sec;
}
int main() {
    std::cout << iof::fmtr("%02s") << Time(1,34,36);
}

would print "01:34:36" to std::cout.

The format specification characters are "object-oriented"

Note that some FSC apply to the whole object being output, whereas others only set format state to be used by the object being output. E.g.:

std::cout << iof::fmtr("10f.4s") << obj; 

If obj is a plain float, that float will be printed in "fixed precision" notation with 4 digits of precision ('f.4'), in a field 10 chars wide. But if obj is an instance of a class that has operator<<(std::ostream&) defined, that whole object will be fit in a field 10 chars wide, and all floats sent to the stream by this object will appear in fixed-point notation with 4 digits precision (unless, of course, the object's operator<< overrides this).

It is straighforward to know if an FSC applies to a whole object or to the members of an object: if the FSC concerns a "fundamental type", such as float (notation, precision, etc), int (base, sign, etc), bool (alpha or not), etc, then the FSC will apply to each member of the object (unless naturally the object is itself a fundamental type). If the FSC is a "generic" concept (e.g., alignment, width), it applies to the whole object.

The full set of output FSC is available.

Supported output formatting operations

The following aspects of formatting output can be controlled:

Recommended order to ease interpretation of format specification by other users. It is roughly the order in which the operations are applied to the output:

[notation][extra][width][align][fill][.precision][.truncation]

Every part of this structure is optional.

Fill characters

Any character between a format marker's opening '%' and closing 's' (or 'S') is deemed an FSC *if* it is understood by iof::fmtr. If not, it is used a fill character. This means you can use any character as a fill character, unlike with printf.

If you need a fill character that is a reserved character, you must "escape" it by preceding it with the format string escape character '!':

std::cout << iof::fmtr("%10fs") << 1.234;

will print a floating-point number in fixed-point notation in a field of 10 chars wide, i.e. " 1.234", whereas

std::cout << iof::fmtr("%10!fs") << 1.234;

will print it in the stream's default notation (general) with 'f' as the fill character, ie "ffff1.234".

Format specification can happen in any order

The FSC in a format marker may specify the output or input format in any order, e.g. alignment, width, notation, etc. The order does not matter because the formatting is done via state flags on the stream.

Read formatted data from a stream (formatted input)

Insert an instance of iof::fmtr between the stream and the objects being read to:

std::cin >> iof::fmtr(format_string) >> obj1 >> obj2 ...;

The format_string and iof::fmtr have the same characterics as for formatted output. Here is the complete set of input format specification characters.

Remaining objects are output "unformatted"

If there are more objects than there are format markers, then the remaining objects are output unformatted (and therefore concatenated).

Format state of stream automatically restored

The format state of stream is automatically restored by iof::fmtr destructor: the stream's format state is restored to what it was when the iof::fmtr was created. This way, your formatting never "bleeds" out into other (and most often unrelated) sections of your program/library.

Notifies you if unused format markers

Unused format markers are considered a programmer error by an iof::fmtr: if there are N format markers in the format string, there must be at least N objects following the iof::fmtr instance. E.g. the following will fail:

std::cout << iof::fmtr("%s");

In a debug build the failure will be a failed assertion, in release build an iof::too_many_markers exception gets thrown. This exception is necessary since the format string is processed at run-time.

You can change this behavior with compiler "defines": EXTRA_MARKERS_NO_ASSERT will not assert, and EXTRA_MARKERS_NO_THROW will not throw an iof::too_many_markers exception (which derives from std::exception).

Save a format state of a stream for future use

This is provided by the iof::stream_fmt class, whose constructor takes a stream:

iof::stream_fmt fmt(std::cout);
std::ofstream yourStream('tmpFile.txt');
fmt.applyTo(yourStream);
assert(iof::stream_fmt(yourStream) == iof::stream_fmt(std::cout));

Note how the format state can be "applied" to another stream, and how format states can be compared. Application overwrites rather than merges.

Automatically restore the format state when unformatted I/O

During input or output, what if an exception occurs? If the output or input is via iof::fmtr, you need not worry since the format state is restored to its original state. But if you don't need formatted output or input, you no longer have that benefit, except if you use iof::fmt_guard:

void f() {

    // format state of std::cout saved on next line:
    iof::fmt_guard guard(std::cout); 

    // now do some output, assume exception gets thrown:
    std::cout << ...; 
    // uh oh, what if some of the objects you output did some stream
    // formatting? Thanks to iof::fmt_guard, no problem:
    
} // format state of std::cout automatically restored here

Format specifications "refactored"

If the format specification comes from an external source (user, database, etc), you would normally (as with printf) have to create the format string yourself, then use the format string in an iof::fmtr. With iof::fmt_spec, this is not necessary:

iof::fmt_spec hex("#0x4"); // 0xnnnn format
std::cout << iof::fmtr("Hi %s\n") << hex << obj1;
std::cout << iof::fmtr("Hi %s\n") << hex(obj1); // same as above

Note that iof::fmt_spec does not "consume" a format marker, since it does not make sense to send an iof::fmt_spec to a stream without an associated object to output. I.e.,

iof::fmt_spec fs1(...), fs2(...), fs3(...); // several format specs
std::cout << iof::fmtr("Hi %s: %s\n") 
          << fs1 << fs2 << fs3 << obj1 << obj2;

Note how there are only two format markers in this example's format string: fs1, fs2 and fs3 will all be combined logically to format obj1, whereas obj2 has not format specification.

Note also that iof::fmt_spec objects are cumulative: fs1 could specify what to do for floating point values, where fs2 could specify what to do for integer values, and fs3 could specify the alignment/width. If fs2 also specifies width, then the width given in fs3 will prevail since it will be processed after fs2.

Output in hexadecimal format

Output in hex using raw streams is not easy. The iof output format specification to use is longer than you would expect for such a simple task: "x#8_0", which translates to:

E.g. if the object is an integer equal to 17 in base 10:

Format specification: Then 17 gets printed as:
x "11"
x "0x11"
#8x "    0x11"
_#8x "0x    11"
_#08x "0x000011"

It may be useful to define an iof::fmt_spec and reuse it:

const iof::fmt_spec hex("x#8_0");
...
std::cout << iof::fmtr("%s\n") << hex(17);

If you need to be able to specify the "width" dynamically, leave it out from hex, and use format composition:

const iof::fmt_spec hex("x#_0");
...
std::cout << iof::fmtr("%s %6s %10s\n") 
          << hex(17) << hex(17) << hex(17);

which will print "0x11 0x0011 0x00000011".

Objects that don't consume format markers

Some objects don't "consume" format markers: iof::fmt_spec and any std manipulator. This is because they operate on the stream state for the next* object output/input. E.g.

std::cout << iof::fmtr("%s = %s") << 
          << iof::fmt_spec("...") << std::setprecision(5) 
          << obj1 << obj2;

where there are only two format markers in the format string, but there are four objects sent to the stream: the first is an iof::fmt_spec and the second a std manipulator, so they both affect the output of obj1. The output of obj2 is unaffected by either since formatting does not persist beyond a marker (unless the marker is closed with 'S' instead of 's').

Verify that text in format string is matched verbatim by input stream

std::string name;
std::cin >> iof::fmtr("hi %s") >> name;

expects the std::cin to have exactly the characters 'hi ' followed by more characters, which will be put into name. A basic pattern matching capability is provied to skip certain sequences of characters, see iof::skip (which does not attempt to provide a full pattern matching capability -- consider using a good regexp library for this).

Exception provides info about cause of error on input

Since reading from a stream is much more likely to produce exceptions than when writing to it, any exception thrown by the stream is caught and a replacement exception is generated, with more information, such as which was the active format marker when the error occurred (1 would imply first one, etc).

Get validity state of input stream

A typical input streaming involves a loop until some error condition occurs. The iof::validity class contains information about the validity state of a stream. It is "filled" with the state of the stream, and automatically converts to a boolean, allowing you to do the following:

std::string val1;
float val2;
iof::validity ok;
while (stream >> iof::fmtr("%s, %s") >> val1 >> val2 >> ok)
    ... get more data into stream (e.g. by reading stdin) ...

The loop will be exited only when the iof::validity evaluates to false (or when an exception gets thrown, e.g. pipe closed etc).

Skip some patterns on input

The STL stream ignore() methods are exposed via "skip markers", which start with '%[' and end with ']'. Any characters in between, known as the "skip specification characters" (SSC), specify what to skip. This does not attempt to provide a full pattern matching capability -- consider doing straight input to a string and then using a good regexp library to filter out the un/desired patterns. E.g.

std::istringstream strm("123456789");
int a = 0;
strm >> iof::fmtr("%[5]%s") >> a;

will skip the first 5 characters input, causing a to have the value 6789.

The complete set of "skip specification characters" (SSC) is available.

Define skip patterns at runtime

The iof::skip_past class allows you to define skip patterns at runtime. A skip pattern occupies a format marker, not a skip marker:

float value=0;
std::cin >> iof::fmtr("%s%s") >> iof::skip_past(10) >> value;

In the above, the first format marker corresponds to the skipper, which will skip some characters from the stream; the the second format marker indicates what to input. You can think of a skip_past as being a proper "dump spot" for data from the stream, but you don't care about the data.

If you prefer function call syntax rather than operator syntax

If for some reason you don't like the operator notation, iof provides function wrappers that this for you:

They can each take up to 10 arguments, which means there can be at most 10 format markers in the format string. If some of the arguments are iof::fmt_spec objects, there will be fewer format markers. Note that in the rare case that the limitation of 10 is inconvenient (which would only happen if for some reason the function call cannot be broken into two parts -- if so I would like to hear from you so I can educate myself), creating a wrapper for N>10 is trivial, just look at the corresponding header file (iof/coutf.hpp for iof::coutf, etc), and do a copy-paste-edit of the N=10 overload.

Persistent Formatting: factoring out common format specs

For readabiity and simplicity, it is often desirable to factor out common format specs. One way of doing this is via iof::fmt_spec objects, but another is via "persistent formatting", which is toggled on when a format marker ends with 'S' instead of 's'.

With persistent formatting, the following

stream << iof::fmtr("%5.2s %5.2s %5.2s %10.2s %10.2s %10.2s\n") 
       << ...;

can be written:

stream << iof::fmtr("%5.2S %s %s %10S %s %s\n") << ...;

The mechanism is very simple:

Flush the stream from format string

Flushing a stream is very a common operation. Often, it is done after the final carriage return of a formatted output is sent to a stream, to ensure the message is visible immediately, so it is done by sending a std::endl manipulator to the stream. But with iof::fmtr, the carriage return is easy to do ('\n'), but what about the flush? Sending a std::flush manipulator is not really "in line" with iof, and requiring that you call stream.flush() is not really nice.

Therefore, the '\f', normally interpreted as a form feed character, is "overloaded" such that when it appears in a format string, it causes flush() to be called on the stream instead of being sent to the stream. E.g.

std::cout << iof::fmtr("header: %s\nfooter: %s\n\f") 
          << header << footer;

is the same as

std::cout << "header: " << header << "\nfooter: "  
          << footer << std::endl;

Note that to output a 'form feed' character to a stream, take it out of the format string and put a format marker where it would be:

std::cout << iof::fmtr("%s") << '\f';

Stringizer: output to a string directly

If a string is to be created, then iof::stringizer (or the iof::tostr() function if you prefer function syntax rather than operator syntax) is convenient:

std::string msg = iof::stringizer(format_string) & obj1 & obj2 & ...;

Note that '<<' can be used instead of '&', but the latter seems more natural since stringizer is not really a stream (though it does contain one).

Note also that iof::stringizer is a good way to allow for a format string created at runtime (eg input by user, database, etc), since it checks that all format markers have been used only when it is converted to a string. You would count how many format markers are in the format string (TODO: add this function to library), then loop that many times, giving one more arg every time:

std::string f()
{
    std::string userFmtString;
    ... put stuff in userFmtString ...
    size_t numMarkers = iof::countMarkers(userFmtString); 
    iof::stringizer ss(format_string);
    
    for (int ii=0; ii<numMarkers; ++ii)
        .. get next obj from somewhere (user input?) ...
        ss << obj; // consume one more marker
        
    return ss; // ss gets converted to a string
}

Steps in output formatting

The process of parsing the format string and sending the objects to the output stream can be summarized as follows. Assume the code is
std::cout << iof::fmtr("Hello %...s, the price is $%...s\n") 
          << name << price);

where "..." indicate format specification characters (which ones specifically does not matter here):

  1. Save format of std::cout stream
  2. Send "Hello " to stream
  3. Call manipulators/methods on stream according to FSC in first marker
  4. Send varible name to stream
  5. Restore std::cout format to saved format
  6. Send ", the price is $" to stream
  7. Call manipulators/methods on stream according to FSC in second marker
  8. Send varible price to stream
  9. Restore std::cout format to saved format
  10. Send "\n" to stream
  11. Restore std::cout format to saved format
  12. Verify that there are no more format markers

Differences with C's printf format markers


Generated on Fri Nov 24 16:16:01 2006 for IOF Library by doxygen 1.5.1-p1
Thanks to SourceForge.net Logo for hosting