Introduction
Description
This utility is designed to mimic the string format arguments used by the C function printf()
. A Format
object takes a "format string" that specify a printable string with conversion specifications introduced by the '%'
character. For each conversion specifications there must also be a matching argument of the correct type.
If there are not enough arguments or they are the wrong type this is a runtime error as the format string is parsed at runtime during the construction of the Format
object.
When the format object is passed to a std::iostream
using the serialization operator operator<<
all the arguments are serialized to the stream according to the conversion specifications.
Create Format
#include "ThorsIOUtil/Format.h"
#include <iostream>
using namespace Fmt = ThorsAnvil::IOUtil;
int main()
{
std::cout << Fmt::make_format("Print Integre >%5.3d<\n", 5); // Print Integre > 005<
}
So how do you build a Format
object?
The easiest way is to use the make_format(<format string> [, argument]*)
. This function takes the format string and the arguments and creates a Format
object using the types of the arguments to construct a Format object of the correct type. Note this can be done by hand, but like std::pair<>
it is simpler to use the utility function.
Testing
This library is tested with the same unit tests used by the GNU libraries to test printf()
.
Conversion Specifications
Format
The conversion specifiers have the following format.
Only the <Specifier>
is required, all other part of the conversion specifiers are optional.
Specifier
Specifier
% %% generates '%'
// The following match integer values.
// will match an int parameter unless modified by the `Length`
d prints a signed integer base 10
i prints a signed integer base 10
o prints an unsigned integer base 8
u prints an unsigned integer base 10
X/x prints an unsigned integer base 16
// The following matches floating point values
// will match a double value unless modified by the `Length`
f prints using a fixed point notation: -?ddd.ddd
E/e prints using expontent notation -?d.ddd[Ee][+-]?dd
G/g prints using fixed point or exponent notation.
A/a prints using a hexidecimal fraction notation. -?0xh.hhh[Pp][+-]?dd
// Charavter types
// Can use the `Length` modifier to change what it matches.
c matches a char input prints an unsigned char
s matches a char* or std::string prints a string of characters
// Other conversions
p matches any pointer prints a hex number prefixed with 0x
n does not match anything prints the number of characters printed
The specifier field basically describes what type of object is being serialized. There are basically four types of object:
- Interger
- Floating Point
- Character (String is a list of characters)
- Pointer
There are two special cases:
%%
: This is the escape sequence and generates a '%' character.%n
: Outputs the number of characters serialized to the output stream.
Example: %d
Flag
Flags: one or more of:
// For integers, floating point, char, string and pointer values
- Left justify output inside the field.
This assumes you specify a `<Width>` field otherwise this will have no affect
// For integers and floating point values
+ Print the '+' symbol for positive values.
<Space> Print the ' ' symbol for positive values (ignored if + also used)
This helps positive and negative values align with forcing the '+' symbol
' Seporate digits into groups specified by LC_NUMERIC
0 Pad number with zero rather than space. Padding comes after sign and base information.
It is used to make sure the serialized output is at least `<Width>` wide.
Ignored if the '-' flag is used.
// For integers values
# Add base information to number (After sign)
For %o values prefix value with '0'
For %x values prefix value with '0x'
For %X values prefix value with '0X'
// For floating point values
# Allways add a decimal point
For %g %G forces trailing zero after decimal point.
The flag field is optional. If used you can use one or more of the flag characters to control how the value is serialized.
Example: %#x
Width
The width is an optional integer that specifies the minimum width of the field we use to print the value. If the value is smaller than the width the field is padded using the padding value (see Flags to change the padding value).
Note: If the value is larger than the specified width it will overflow the field (i.e. no cutting is done to truncate the value to the field width).
Example: %15d
Precision
Precision:
// For integers values
The minimum number of digits used in the number.
Leadding zero(s) are added to achieve the minumum precision required.
If no value given then print as many digits as required.
If precision is zero and the value is zero then no value is printed.
// For floating point values
The precision is the number of digits following the decimal point
An optional field. A number prefixed by the '.' character. For integer and floating point values defines the number of digits that will be used.
Example: %.8f
Length
Length:
// The specifiers d/i/o/u/x/X match integer values.
// Unless proceeeded by one of the following length specifiers.
// That allow us match against alternatives object types.
hh matches a char or unsigned char parameter
h marches a short or unsigned short parameter
l matches a long or unsigned long parameter
ll matches a long long or unsigned long long parameter
L matches a long long or unsigned long long parameter
q matches a long long or unsigned long long parameter
j matches a std::intmax_t parameter
t matches a std::ptrdiff_t parameter
z matches a std::size_t parameter
// The following match floating point values unless modified by the Length parameter
L matches a long double parameter
// The following match char and string conversions.
// rather than `char` the base of the char/string will be std::wchar_t
The <Length>
specifier changes the type of value that is bound to the specifier.
Example: %ld
Notes
#include "ThorsIOUtil/Format.h"
#include <iostream>
using namespace Fmt = ThorsAnvil::IOUtil;
int main()
{
short shortValue = 10;
std::cout << Fmt::make_format("Print short >%hd<\n", shortValue);
std::cout << Fmt::make_cppformat("Print short >%hd<\n", shortValue);
int intValue = 10;
std::cout << Fmt::make_format("Print short >%hd<\n", intValue);
std::cout << Fmt::make_cppformat("Print short >%hd<\n", intValue); // This will throw a runtime error.
}
In C char
and short
values are converted to int before being passed as arguments to printf()
. Thus the <Length>
fields hh
and h
will convert the int back to the appropriate type (char, short) before printf()
serializes the value. This has the side affect that you can pass int
values to printf()
and it will serialize correctly.
To conform with C
we also allow int
values to bind to %hd
and %hhd
conversion specifiers by default. But we also provide a stricter interpretation for C++ users that only allows char
to bind to %hhd
values and short
to bind to %hd
values.
If you use make_format()
the formatter uses C compatibility mode and will match %hhd
and %hd
against int
values in addition to the match type expected.
If you use make_cppformat()
the conversion specifiers use the stricter C++ type mode.
APIDocs
- NameSpace:
- ThorsAnvil::IOUtil
- Headers:
- ThorsIOUtil
- Format.h
-
- Format<Args...> make_format()
- Format<Args...> make_cppformat()
make_format
Builds a Format
object based on the types of the arguments.
make_cppformat
Builds a Format
object based on the types of the arguments.
This version has slightly tighter restrictions on matching parameters to conversion specifiers.
Like C++ the type system is much pickier than C.
Internal
- NameSpace:
- ThorsAnvil::IOUtil
- Headers:
- ThorsIOUtil
- Format.h
-
- Format
- FormatInfo.h
-
- FormatInfo
- Formatter.h
-
- NormalizeChar
- Formatter
- SignConversionOption.h
-
- SignConversionOption
- SignConversionOption<char>
- SignConversionOption<short>
- SignConversionOption<int>
- SignConversionOption<long>
- SignConversionOption<long long>
- SignConversionOption<unsigned char>
- SignConversionOption<unsigned short>
- SignConversionOption<unsigned int>
- SignConversionOption<unsigned long>
- SignConversionOption<unsigned long long>
- printIntToStream.h
-
- void printIntToStream()
- printStringToStream.h
-
- void printStringToStream()
- printToStream.h
-
- CharIntConverter
- CharIntConverter<char>
- CharIntConverter<unsigned char>
- void printToStream()
- void printToStream()
- void printToStream()
- saveToStream.h
-
- void saveToStream()
- void saveToStream()
Format
This is the object returned by make_format() and make_cppformat()
This is the object that is passed to the std::ostream
via operator<<
.
Internally it keeps the original string and references to all the parameters that
need to be serialized. When the object is constructed (at runtime) the conversion
specifiers in the string are validated against the actual parameters to make sure
the correctly match.
FormatInfo
Each conversion specifier in the format string is parsed and converted into an object of this type.
NormalizeChar
Char is a very special class.
The type char
can be either signed char
or unsigned char
and is its own specif type (unlike int).
type for all classes except signed char
which is converted to char
.
Formatter
Parses a string segment.
It saves the static prefix before a conversion specifier.
Then it saves a conversion specifier into the info
member.
It keeps track of the number of characters parsed in used
.
SignConversionOption
When handling integer types some
automatic conversions are allowed.
This type handles these conversions.
It is used by Formatter::apply()
SignConversionOption<char>
Specialization of SignConversionOption
SignConversionOption<short>
Specialization of SignConversionOption
SignConversionOption<int>
Specialization of SignConversionOption
SignConversionOption<long>
Specialization of SignConversionOption
SignConversionOption<long long>
Specialization of SignConversionOption
SignConversionOption<unsigned char>
Specialization of SignConversionOption
SignConversionOption<unsigned short>
Specialization of SignConversionOption
SignConversionOption<unsigned int>
Specialization of SignConversionOption
SignConversionOption<unsigned long>
Must have some description
SignConversionOption<unsigned long long>
Must have some description
printIntToStream
Given an argument arg
(which is one of the int types) and a format info
serialize it to the stream s
printStringToStream
Given an argument arg
and a format info
serialize it to the stream s
printToStream
Template method for strings
printToStream
Template method for strings
printToStream
Template method for strings