Home
Name Modified Size InfoDownloads / Week
xprintf-0.3.1.zip 2014-07-02 114.6 kB
xprintf-0.3.1.tar.gz 2014-07-02 91.2 kB
README.md 2014-07-01 22.8 kB
Totals: 3 Items   228.6 kB 0

Xprintf - printf for C++11

Contents

Introduction

This is an implementation of printf for C++11.

  • Full standard C printf() format support, most of Posix and X/Open extensions like positional parameters, seamless integration of C++ strings.
  • Full type safety, respects the type of the arguments. Argument size specifiers like l are accepted but mostly ignored, since the type of the arguments is consulted for the size.
  • Seamless support for arbitrary combinations of narrow and wide output streams and output strings, narrow and wide format strings, narrrow and wide arguments.
  • Seamless support for new char16_t and char32_t character and string types using the included StrCvt package for character type conversions.

In C, the functions from the printf() family are used for formatted text output and string formatting.

C++ replaced this with the type-safe output stream model and its << operator, which offers the same formatting options but in a much more verbous way.

This library implements the well-known printf() interface for C++, with full type safety. It supports all the format strings of the standard C library printf(), plus Posix extensions like positional arguments. Example syntax:


    // Write to stream
    std::cout << xprintf("%d: %f", 1, 5.7) << std::endl;
    // Format to string
    std::string s = xprintf("Result: %d", 42);
    // Or to wide character string
    std::wstring w = xprintf("Question: how much, %s", s);
    // Default: output goes to standard output
    xprintf("%s\n", w);

Installation

Unpack the distribution to a directory on your local machine. You can include the proper header in subdirectory include/xprintf from your program. To make inclusion of the headers easier, it is recommended to add the subdirectory include of the Xprintf distribution to the include file search path of the compiler. This is commonly achieved with the option -I/path/to/xprintf/include (assuming that the xprintf distribution has been upacked to directory /path/to/xprintf). Then you can include the xprintf headers through their standard names like "xprintf/xprintf.h".

Usage

In order to use Xprintf, the proper headers have to be included. These are:


    // Normal xprintf() header:
    #include "/path/to/xprintf/include/xprintf/xprintf.h"
    // For use with FILE* as stream argument:
    #include "/path/to/xprintf/include/xprintf/xprintf_file.h"
    // For the standard function names like printf() and fprintf();
    #include "/path/to/xprintf/include/xprintf/xprintf_std.h"

If the xprintf include directory /path/to/xprintf/include has been added to the include file search path of the compiler, e.g. using the compiler option -I/path/to/xprintf/include, this is reduced to:


    #include "xprintf/xprintf.h"
    using namespace XPrintf;

The xprintf() functions are exported through namespace XPrintf. It is recommented to make them available via a using namespace directive like in the example above. Alternatively it is possible to import the functions xprintf() and sxprintf() separately through using declarations:


    using XPrintf::xprintf;
    using XPrintf::sxprintf; // Optional

Header "xprintf/xprintf_std.h" defines the C standard library names fprintf(), printf(), snprintf(), sprintf(), fwprintf(), wprintf() and swprintf() in namespace XPrintfStd. To import them into your working namespace, you can again use a using namespace directive:


    #include "xprintf/xprintf_std.h"
    using namespace XPrintfStd;

    printf("Hello, world!\n");

This makes all the standard library names available through their standard names.

Xprintf() is pre-configured for header-only use. This means: Just include the proper header and you are done. In order to reduce space overhead and compilation time, a precompiled library can be used. See section Creating a Library.

Xprintf internally uses the included StrCvt library for character type conversions of arguments and destination strings. If you want to use the StrCvt package directly, look there for information about Charcvt headers and namespaces.

xprintf(format, args...)

The most flexible way to use xprintf() is through the call xprintf(format, args...). The expression xprintf(format, args...) can be used as follows:

  • It can be used as argument to operator<<, which writes the formatted result to the output stream.
  • It can be assigned to a string or wstring, which receives the formatted output.
  • The output can be left unused. In this case it is written to the standard output (or another stream, see below).

The diffent usages are shown here:


    // Write to stream:
    std::cout << xprintf("%d: %f", 1, 5.7) << std::endl;
    // Or to wide stream:
    std::wcout << xprintf("%d: %f", 1, 5.7) << std::endl;
    // Format to string:
    std::string s = xprintf("Result: %d", 42);
    // Or to wide character string:
    std::wstring w = xprintf("Question: how much, %s", s);
    // Default: output goes to standard output (can be redirected)
    xprintf("%s\n", w);
    // Using a wide character format works as well.
    xprintf(L"%s\n", w);
    // Redirect to another stream:
    xprintf(std::wcerr);
    // Now by default all output goes to std::wcerr
    xprintf("%s\n", w);

Narrow and wide character types, strings and streams can be freely mixed:

  • The format can be a char or a wchar_t character string constant or a string or wstring.
  • Independently, the output stream can be a narrow or a wide character stream, and the string assigned from the result can be a narrow or a wide string.

Generally all argument strings are converted (using the included StrCvt library) according to the conversion rules of the currently installed global locale. For this conversion, narrow character strings are treated as locale-defined characters. If strings are known to be in UTF-8 format instead of the locale-defined character format, they can be defined as type StrCvt::u8string. Xprintf recognizes this string type and transforms these arguments according to the UTF transformations.

Redirect default output stream

If xprintf() is called with only a stream argument like above in xprintf(std::wcerr), this stream becomes the default stream instead of the standard output.

xprintf(stream, format, args...)

An output stream can be passed to xprintf() as the first argument. Then the output is sent to this stream, and xprintf() returns the number of characters formatted (which may differ from the number of bytes written if e.g. the format is a wide character format and the stream a narrow char stream).


    // Write to stream:
    int count = xprintf(std::cout, "%d: %f\n", 1, 5.7);
    // Or to wide stream:
    count = xprintf(std::wcerr, "%d: %f\n", 1, 5.7);

If the header "xprintf/xprintf_file" has been included, xprintf() works identically with FILE* streams like stdout and stderr. This call is equivalent to the standard C function fprintf().

sxprintf(format, args...)

While the result of xprintf() can be assigned to a string or a wstring, it isn't one. To pass the result to a function as a string argument, the conversion might need to be done by an explicit call to the string constructor.

The function sxprintf(format, args...) returns a string directly. The default string type (string or wstring) is determined by the width of the format argument. It can be overridden by a template argument.


    // Convert result of xprintf(format, args...) to a string
    my_fun(std::string(xprintf("Result is %d", 42)));
    // Simpler: use sxprintf(format, args...)
    my_fun(sxprintf("Result is %d", 42));
    // Pass wstring to my_fun(), using template argument
    my_fun(sxprintf<std::wstring>("Result is %d", 42));
    // Equivalent: use wchar_t format
    my_fun(sxprintf(L"Result is %d", 42));

Using sxprintf(format, args...) should in theory be a little bit faster than the otherwise equivalent form `std::string(xprintf(format, args...)) since no proxy has to be created.

Creating a Library

Xprintf is preconfigured for header-only use. This means: Just include the proper header and you are done. In order to reduce space overhead and compilation time, a precompiled library can be used.

The main advantage in using a library is that each time the xprintf header is included, the compiler need not look at the implementation details. This can speed up compilation significantly.

To create the library, the C++ source files libxprintf.cpp and libxprintf_file.cpp in directory lib of the distribution must be compiled. Under Linux, just run make. Before compiling, you may want to select the compiler to use: Uncomment to proper CXX= - line in the toplevel Makefile.template. Running make should create a library lib/libxprintf.a, which has to be linked to the programs.

In Visual C++, instead of building a library, you may just add the library source files to your project.

In order to make the header use the library, you must open the header xprintf/xprintf.h with an editor and change the preprocessor symbol XPRINTF_IMPL_USE_LIBRARY from 0 to 1. The next time a program is compiled, the library will be used. You can check that the library is used as intended by omitting the library when linking. Linking should fail with missing externals.

In order to run the tests, the headers for the boost test framework are required.

Compatibility

All portable standard C printf() formats are supported and should yield the identical result as with the standard C printf().

Most Posix printf() extensions like positional arguments are supported.

The following additional format declarators are supported:

  • %B formats boolean values, flag # disables boolalpha

  • %m formats monetary values, flag # shows currency sign

  • %M formats monetary values, flag # shows international currency name

The char type used internally for formatting is determined by the format char type. The result should not be affected (besides the character count returned). Wide and narrow format strings, output streams, strings and arguments can be freely mixed. Arguments are converted to the format char type as needed, and the result is converted to proper stream or string type used.

Xprintf() has been tested with:

  • GCC 4.7, 4.8 and 4.9 on Linux
  • Visual Studio Express 2013 for Windows Desktop with November 2013 CTP
  • Intel C++ 14.0.2 on Linux
  • Clang 3.5.0 on Linux

Format String, Conversion Specifications

Xprintf writes output to a (wide or narrow) output stream under control of a (wide or narrow) format string that specifies how the remaining arguments are converted. If there are insufficient arguments for the format, the program is terminated through a call to assert() describing the problematic format string.

Characters other than % in the input stream are copied unchanged to the output stream.

The character % begins a conversion specification. A conversion specification consists of the following parts:

  • An optional argument number specifier in the form n$, with n a decimal integer number denoting the index (after the format string) of the argument to be formatted. If the argument number specifier is missing, the next consecutive argument is formatted.

  • Zero or more optional flags in the form of the characters -, +, space, #, 0 or '.

  • An optional minimum field width in the form of a decimal number, an asterisk * or *n$ with n a decimal integer number denoting an argument index (see below). If the converted value requires less width than the specified number of space characters, it is padded with space characters on the left side (by default) or on the right side if the left adjustment flag - has been specified.

  • An optional precision in the form of a period . followed by an decimal number, an asterisk * or *n$ with n a decimal integer number (see below).

  • An optional argument size modifier in the form hh, h, l, ll, j, z, t or L.

  • A final conversion specifier that specifies the type of the conversion to be applied.

As noted above, the field width and the precision may be indicated by an asterisk *, in which case the next argument in sequence (before the value to be converted) must contain an integral number specifying the value. A negative field width implies a - flag. If the form *n$ is used, the decimal integer n gives the index of the argument (after the format) containing the width or precision.

The flags and their meanings are:

  • - Left-justify the result of the conversion. If this flag is not specified, the conversion is right-justified.

  • + Format a positive value with a + sign. If this flag is not specified, the sign is omitted for positive values.

  • space For a positive value, output a space at the place of the sign. If the space and + flags both appear, the space flag is ignored.

  • # The result is converted in an alternative form. For o, x and X conversion specifiers, the number is prefixed by 0, 0x or 0X. For f, F, e, E, g, G, a and A conversion, the result is always formatted with a decimal point character, even if no digits follow it. For g and G conversions, trailing zeros are not removed from the result. For the bool conversion specifier B, write 0 or 1 instead of false and true. For the monetary conversion specifiers, write the currency symbol.

  • 0 Use leading zeros to pad to the field width instead of spaces.

  • ' Insert thousands separators. This flag is accepted for compatibility with the X/Open printf() specification but ignored by xprintf(). Insertion of thousands separators is dependent on the locale.

The argument size modifiers denote that the argument to be formatted is a (signed or unsigned) char for hh, short for h, long for l, long long for ll, intmax_t for j, size_t for z, ptrdiff_t for t or long double for L. They are supported for compatibility with the C version of printf() but mostly ignored by xprintf(). Xprintf() gets this information from the type of the arguments. The h and hh argument size specifiers are used with the conversions specifiers o, x and X to mask leading bits from signed numbers. The l argument size specifier is used with conversion specifier c to convert a wide character.

The conversion specifiers and their meanings are:

  • d, i, u

Format the argument as a decimal integer. These conversion specifiers are equivalent, signedness is determined from the type of the argument. The argument must be of a signed or unsigned integral or a floating point type. The precision specifies the minimum number of digits.

  • o, x, X

Format the argument as a octal or hexadecimal number. Specifier X uses uppercase hexadecimal letters. The argument must be of a signed or unsigned integral type, a floating point type or a pointer type. The precision specifies the minimum number of digits.

  • f, F

Format the argument in plain floating point style [-]dddd.ddd. The precision specifies the number of digits to the right of the decimal point and defaults to 6. The argument must be of a floating point type or a signed or unsigned integral type.

  • e, E

Format the argument in scientific floating point style [-]d.dddddde[+/-]dd. The precision specifies the number of digits to the right of the decimal point and defaults to 6. The argument must be of a floating point type or a signed or unsigned integral type.

  • g, G

Format like the e or E specifier if the exponent is less than -4 or greater than or equal to the precision, else format like the f or F specifier.

  • a, A

Format the argument in hexadecimal floating point style [-]0xh.hhhhhhp[+/-]d. The precision specifies the number of digits to the right of the decimal point and defaults to the number of digits necessary to exactly represent the argument. The argument must be of a floating point type or a signed or unsigned integral type.

  • c, C

Convert to a single character. If C is used or the argument size specifier l is present, the argument is interpreted as a wide character. Else the argument is truncated to char. The argument must be of a signed or unsigned integral type.

  • s, S

Format the argument as a string of characters. The argument must be a C++ string type (std::string, std::wstring, std::u16string or std::u32string) or a C style null-terminated char, wchar_t, char16_t or char32_t character pointer string. For the C++ string types, the precision specifies the maximum number of Unicode characters to output. For the C style null-terminated character pointer string types, the precision specifies the maximum length of the string; if no null-character is found after this number of source characters, precision source characters are output. For padding, the width of the formatted string is estimated from the Unicode characters using Markus Kuhn's mk_wcswidth().

Generally all argument strings are converted (using the included StrCvt library) according to the conversion rules of the currently installed global locale. For this conversion, narrow character strings are treated as locale-defined characters. If strings are known to be in UTF-8 format instead of the locale-defined character format, they can be defined as type StrCvt::u8string. Xprintf recognizes this string type and transforms these arguments according to the UTF transformations.

  • p

Format the argument in an implementation-defined pointer format. The argument must be a pointer or a signed or unsigned integral type.

  • B

Format the argument as a bool. The argument must be a bool, a signed or unsigned integral type, a floating point type or a pointer type.

  • m, M

Format the argument as a locale-dependent monetary value. Declarator M uses the international currency symbol. Note that in order to write the currency symbol, flag # must be specified. The argument must by a signed or unsigned integral type or a floating point type.

  • n

Do not output the argument but write the number of characters converted up to this point to the argument. The argument must be a pointer to a signed or unsigned integral value.

If the argument type is not usable with the conversion specificator, the program is terminated through a call to assert() describing the problematic format string.

Problems, Incompatibilities and Bugs

  • The most likely problem may be that for the "%s" and "%S" formats with a pointer argument, the type of the pointer argument is respected. With C printf(), the actual type of the pointer argument is ignored, and the format string determines whether it is interpreted as a char* or a wchar_t*. Xprintf interprets the argument diffently based on whether it is passed a char*, a wchar_t*, a char16_t* or a char32_t*.

  • Xprintf does not support the Posix flag ', which means to format using thousands separators. With C++, this is dependent on the locale used and can not be switched on or off case by case.

  • With the %s format, the handling of precision and width in format specifiers differs from that by C printf(). The C printf() function counts bytes. This means, blanks are added for a specified width until the specified number of bytes is reached. If the string contains multibyte characters, and is then printed in a fixed-size font, that can result in misaligned output. Xprintf pads to the specified width of the Unicode string.

  • Even worse, if a precision is specified, the C printf() function chops at the specified number of bytes, whithout regard to multibyte character bounds. Xprintf truncates to the specified number of Unicode characters if the argument is one of the C++ string types like std::string or std::wstring. An exception is output from C style null-terminated string pointers (char*, wchar_t* etc.): Since these are possibly used to output from raw memory, the precision is here interpreted as the specified number of source characters.

  • Format declarators %d and %u are interpreted by xprintf() in the identical way. The type of the argument is not converted to signed or unsigned type. If the argument is a floating point number, the floating point number is formatted in integral format. In each case, the value of the argument, whether signed or unsigned, is preserved and correctly represented in the output.

Bugs

  • The standard I/O functions in header xprintf/xprintf_std.h are not selected by the function overload resolution if only the format string and no further arguments are specified. In this case argument dependent lookup selects the standard functions in namespace ::.

License

Copyright (c) 2014 Ruediger Helsch; All rights reserved

Permission to use, copy, modify, and distribute this software for any purpose and without fee is hereby granted. The author disclaims all warranties with regard to this software.

Source: README.md, updated 2014-07-01