Skip to content
/ fmt Public

fmt::printf as extensible C++ replacement for std::printf

License

Notifications You must be signed in to change notification settings

afborchert/fmt

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fmt

fmt::printf as extensible C++ replacement for std::printf

Summary

This header-only C++11 package provides fmt::printf which is intended as a type-safe and extensible drop-in replacement for std::printf. The principal idea is to replace

#include <cstdio>
// ...
int count = std::printf("%d\n", val);
count = std::fprintf(stderr, "%s\n", errmsg);
count = std::snprintf(buf, sizeof buf, "%d", val);
count = std::wprintf(L"%d\n", val);

by

#include "printf.hpp"
// ...
int count = fmt::printf("%d\n", val);
count = fmt::printf(std::cerr, "%s\n", errmsg);
count = fmt::snprintf(buf, sizeof buf, "%d\n", val);
count = fmt::printf(L"%d\n", val); // goes to std::wcout

where the behaviour is expected to be identical with the exception that fmt::printf prints to extensions of basic_ostream instead of FILE* and that the locale of the output stream is used instead of the global C locale.

Thanks to the variadic templates of C++11, fmt::printf provides the functionality of std::printf in a typesafe way. Consequently, it no longer matters for fmt::printf whether you use "%f", "%lf", or "%Lf" as format as the associated operand type is well known. fmt::printf is extensible as all operand types are supported for which an <<-operator exists. Example for std::complex:

std::complex c = /* ... */;
fmt::printf("c = %20.4g\n", c);

fmt::printf uses C++ I/O format flags but makes sure that the previous state of the output stream is restored to its original state after its invocation. Any previous state is ignored, i.e. fmt::printf("%x", val) will print val in hex even if std::cout << std::oct has been used before, and the previous octal conversion preference will stay in effect for <<-operators after the invocation of fmt::printf.

This implementation aims to support faithfully all features of std::printf according to ISO 9899:2011 and IEEE Std 1003.1, 2013 (POSIX). Other important points were to keep it small and header-only. These goals have been more important than performance.

This implementation is based on the C++ standard library and its existing conversions. Some work has been spent to work around incompatibilities between C und C++ in regard to formatted printing. There exists, however, one known exception where fmt::printf diverts from standard behaviour of std::printf. The problem is the combination of "%a" with a precision, e.g. "%.2a". This odd combination is not supported by C++11 (see 22.4.2.2.2 in ISO 14882:2011) but supported by std::printf (see 7.21.6.1 in ISO 9899:2011). But it may be questioned how common or useful it is to combine this.

In particular, fmt::printf supports

  • the return value, i.e. the number of bytes written,
  • the precision is honored by %s and integer conversions (in contrast to std::setprecision which is honored by floating point conversions only)
  • conversions that take the n-th argument, e.g. print("%2$s, %1$s\n", "world", "Hello");, and
  • %n conversions that take an int* where the number of bytes written so far is stored.

Note that fmt::printf, much like std::printf, is locale-dependent. But std::printf is based on the global C locale whereas the behaviour of fmt::printf depends on the locale of the output stream. One notable exception is grouping. This is supported by the C++ locale system but not by C. Instead std::printf provides the apostrophe format flag that asks for a grouping with thousands. To conform to std::printf behaviour, the grouping by fmt::printf depends solely on the use of the apostrophe flag, not on the locale.

License

This package is available under the terms of the MIT License.

Files

To use fmt::printf, you will need just to drop printf.hpp within your project and #include it.

The source file test_suite.cpp is a test suite testing fmt::printf against std::printf and the Makefile helps to compile it.

Alternatives

This is not the first attempt to provide printf look and feel in a type-safe way for C++. There exist numerous other implementations. I want to name a few:

  • In 1994, Cay S. Horstmann published an article about extending the iostreams library in C++ Report where he proposed setformat which takes one format specification and configures the stream accordingly like other manipulators. Example from his paper:

    cout << "(" << setformat("%8.2f") << x << "," << setformat("8.2f") << y << ")" << endl;

  • The Boost Format library offers an approach that permits the grouping of multiple conversions. As the %-operator is used, it does not depend on variadic templates. Example:

    std::cout << boost::format("(x, y) = (%4f, %4f)\n" % x % y;

  • Zhihao Yuan proposed a C++ standard extension providing a printf-like interface for the C++ streams library that is likewise available at Github. Example:

    std::cout << std::putf("(x, y) = (%4f, %4f)\n", x, y);

  • C++ Format by Victor Zverovich provides a library which is no longer header-only with two APIs, one of them offering format strings as in Python. Example:

    fmt::print("I'd rather be {1} than {0}.", "right", "happy");

About

fmt::printf as extensible C++ replacement for std::printf

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published