iof/io.hpp
iof/fmtr.hpp
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.
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;
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.
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.
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.
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".
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.
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).
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.
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
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.
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".
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').
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).
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).
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.
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.
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.
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:
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';
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 }
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):
int width = <something obtained at runtime>; std::cout << iof::fmtr("%s") << std::setw(width) << obj;