nanofmt
nanofmt aims to provide a lite-weight semi-compatible implementation of the excellent fmtlib. This can be used in environments or team cultures where neither std::format nor fmtlib are available for use.
Contents
Basic Usage
Example usage of writing a string with two variables formatted into the output.
char buffer[128];
nanofmt::format_to(buffer, "Hello, {0}! You are visitor {1}.",
UserName,
VisitorCount);
See also our Detailed Examples.
API Overview
The crux of the API is writing to a char
arrays. Writing directly to an array
just works:
char buffer[128];
format_to(buffer, "Format this {}", 128);
A pointer to a buffer and a length can also be provided for safe writing.
format_to_n(buffer, size, "Format this {}", 128);
The format functions all return a char*
pointing to the terminating NUL that
is always written to the buffer.
char* end = format_to(buffer, "{} is {}", seven, 7);
size_t const size = end - buffer;
The nanofmt functions understand the same positional arguments and most format flags of std::format.
format_to(buffer, "{1} then {0}", "Second", "First");
// buffer: First then Second
If the length of formatted text is required, e.g. for allocating buffer space,
the nanofmt::format_length()
function can be used:
size_t const length = format_length("{} plus {}", 7, 7);
char* dest = (char*)malloc(length + 1/*NUL byte*/);
format_to_n(buffer, length + 1, "{} plus {}", 7, 7);
nanofmt also includes implementation of the C++17 standard to_chars
functions, for codebases that are unable or unwilling to use the standard
versions, or who are using older compilers that lack support for
floating-point std::to_chars
See the API for more in-depth coverage of the nanofmt facilities.
The Case for nanofmt
nanofmt may be a good fit for teams or codebases which are unable or unwilling to use fmtlib or std::format, particularly if the reasons involve compilation time or standard library header dependencies.
The only headers nanofmt relies on are <type_traits>
, <cstddef>
,
<cstdint>
, and <cmath>
.
Teams that might otherwise continue to prefer using snprintf
may find
that nanofmt is far more to their tastes.
Anyone unsure of whether they should use nanofmt as their should almost
certainly consider using fmtlib or std::format
instead.
Both fmtlib and the standard formatting facilities offer far more features, far more idiomatic C++ support, and integrate far better with both the rest of the C++ ecosystem and core IO.
Detailed Examples
Custom Types
Provide a specialization of nanofmt::formatter
to enable nanofmt to consume
values of a custom type.
struct MyType {
std::string FirstName;
std::string LastName;
};
namespace nanofmt {
template <>
struct formatter<MyType> {
bool reverse = false;
constexpr char const* parse(char const* in, char const* end) noexcept;
inline void format(MyType const& value, buffer& output) noexcept;
};
}
char buffer[128];
format_into(buffer, "Greetings {:r}!", MyType{"Bob", "Bobson");
// buffer would contain:
// Greetings Bobson, Bob
What does that :r
do? That’s a custom formatter flag supported by the
MyType
formatter. A possible implementation:
constexpr char const* nanofmt::formatter<MyType>::parse(char const* in, char const* end) noexcept {
if (in != end && *in == 'r')
++in;
reverse = true;
}
return reverse;
}
void nanofmt::formatter<MyType>::format(MyType const& value, buffer& output) noexcept {
if (reverse)
format_into(output, "{}, {}", LastName, FirstName);
else
format_into(output, "{} {}", FirstName, LastName);
}
Length-Delimited Buffers
nanofmt automatically length-delimits any attempt to write to a char[] array. When using raw pointers, or to futher constrain the output length, the format_into_n functions may be used.
The format functions return a pointer to the last character written (excluding the NUL byte). This can be used to chain format calls, or to calculate the written length.
char* ptr = GetBufferData();
size_t size = GetBufferSize();
char* end = format_into_n(ptr, size, "Format this! {}", value);
size_t const length = end - ptr;