Here is what you get as briefly as I can say in code. _snprintf(buf, n, %d %u %ld %lu, i, ui, l, ul); becomes oformatstream ofs(%d %u %ld %lu, &a_string_stream); ofs << i << ui << l << ul;
Introduction
I'm quite sure there will be some who will ask "Why did you build this?". Yes, it is slower than printf
and a little fatter than STL wcout
, but...
There are two major reasons I built this template class, apart from the learning experience.
On the one hand, I find the STL manipulators to be cumbersome, on the other, the printf
family of functions have a major drawback, they don't allow arbitrary growth of the output string
.
I could have wrapped only the ostrstream
class but that would have unnecessarily limited the use of this template for other kinds of output, namely file and console.
The main template class, basic_oformatstream
, acts as a shim ("something thin placed between two parts to make a fit") between the user and the ostream
the object is tied to.
WARNING: There is a bug in the VC++ 6.0 STL implementation from Dinkumware.
See the end of file oformatstream.cpp for a short description of the bug (with code). An initial fix is to make the buffer size much larger in <xlocnum>
template num_put
functions virtual _OI do_put(_OI _F, ios_base& _X, _E _Fill, double _V) const
and virtual _OI do_put(_OI _F, ios_base& _X, _E _Fill, long double _V) const
. If making this kind of change to a standard template library file fills you with dread, I don't blame you, so if you can't or won't fix this, then never ever try to output a really big floating point value without using the scientific format, i.e., %e
or %g
, the other one %f
could cause you grief.
So How Do You Use It ?
First, construct an oformatstream
by giving it a format and an output stream to write to. Then it is just a matter of outputting values to the resulting oformatstream
. The order in which you send the output values is, as you should expect, utterly dependent upon the order that you specify in the format string
.
You can change the format string
by using the reformat manipulator which takes a basic_formatter
object as its argument.
formatter f("%8d %6.2f");
oformatstream ofs("%8x %5.3e",&std::cout);
ofs << reformat(f);
Note that formatter
and oformatstream
are typedef
s similar in purpose to those done for cout
and wcout
. In case you were wondering, woformatstream
and wformatter
are the wide equivalents.
oformatstream ofs("[%s] [%8d] [%6.5f]\n", &std::cout);
ofs << "example" << 1 << 3.141592 << setformat;
oformatstream ofs("[%s] [%8d] [%6.5f]", &std::cout);
ofs << "example" << 1 << 3.141592 << setformat << endl;
format_specification fs(12 ,2,std::ios_base::dec);
formatter format("[%s] [%d] [%f]", fs);
oformatstream ofs(format, &std::cout);
ofs << "example" << 1 << 3.141592 << endl;
For some really complex examples, see TestFormat.cpp in the demo.
The following are more detailed descriptions of the template classes and the various implementation classes and functions you will find if you dig around inside oformatstream.hpp.
basic_oformatstream
Constructors
- The default constructor. Will require a call to
tie()
before output will work:
basic_oformatstream()
- Use a format
string
to construct the internal format field vector:
basic_oformatstream(const std::basic_string<_E,_Tr>& s, _Myostream *os = NULL)
- Use a preconstructed
basic_formatter
which allows for a custom default format specification:
basic_oformatstream(const basic_formatter<_E>& f, _Myostream *os = NULL)
Public Methods
- Inserter operators:
_Myt& operator<<(T) for T= {bool,int,long,...etc.}
- Sets the internal formatter:
void formatter(constbasic_formatter<_E,_Tr>& f)
- Returns the internal formatter:
basic_formatter<_E>& formatter()
- Ties to an output stream:
void tie(_Myostream *os)
- Returns the tied output stream:
_Myostream* get_ostream()
- Sets the default format specification:
void default_format_specification(const format_specification& f)
- Returns the default format specification:
format_specification default_format_specification()
Plus several others which are used internally but need to be public
.
basic_formatter
Constructors
- The default constructor:
basic_formatter()
- Supply a default format specification:
basic_formatter(const format_specification fs)
- Use a format
string
:
basic_formatter(std::basic_string<_E,_Tr> fs)
- Use a format
string
and supply a default format specification:
basic_formatter(std::basic_string<_E,_Tr> s, const format_specification& fs)
Implementation Classes
format_flags
Manages changes to a std::ios_base::fmtflags
value. Enforces restrictions such as hex, dec and oct being mutually exclusive flags. Provides operators |=
, &=
, =
and various constructors and conversion operators.
format_specification
A single field's format specification corresponds to a single field, i.e., "%7.5f
".
TEMPLATE CLASS format_characters
Provides a central place for storing constants required by the parsing routines. Currently _E
may be either char
or wchar_t
.
TEMPLATE CLASS basic_formatterfield
Holds the final results of parsing a single field's format specification. These being a prefix text string
and format specification for the field.
TEMPLATE CLASS FormatFieldVector
derived from std::vector< basic_formatterfield<_E,_Tr>>
Parsing Routines
std::ios_base::fmtflags format_flag_from_char<_E>(const _E ch)
Converts the type character [cdefgisx]
into appropriate ios_base
flag values.
bool parse_format_specification<_E>(
std::basic_string<_E>::const_iterator& it,
std::basic_string<_E>::const_iterator& end,
format_specification& outfs, bool& widthset, bool& precset, _E& fillchar)
Called by parse_field<_E>()
to process a single field's format specification, i.e., everything after the percent (%
) symbol.
bool parse_field <_E>(
std::basic_string<_E>::const_iterator& it,
std::basic_string<_E>::const_iterator& end,
basic_formatterfield<_E>& outff,
format_specification& default_fs)
Called by parse_format<_E>
to process a format field, i.e., the prefix text followed by a format specification. Calls parse_format_specification<_E>()
:
bool parse_format<_E>(
std::basic_string<_E>fs,
FormatFieldVector<_E>& ffv,
format_specification& default_fs)
Used by basic_formatter<_E>
constructors to process a full format specification. Calls parse_field<_E>()
to build a basic_formatterfield
which it then stores in a FormatFieldVector
.
Format Output Management Classes
TEMPLATE CLASS basic_formatter
The format of each field is controlled by the given format string
.
TEMPLATE CLASS basic_oformatstream
Outputs values to the connected stream
(does nothing if not). The internal basic_formatter
object controls the layout of each output field and the order that they are expected. No exceptions are thrown if the supplied field type does not match the expected format. The output will probably just look awful.
History
- 14th November, 2001: Initial post
- 26th September, 2022: Mentioned upgrade on Reddit and wanted to update the code to reflect the effort