Ch11.9: Relationship with String
One System, Everywhere
The concat_fast_io and to<T> APIs introduced
in Ch9.12 are not a separate
string-formatting pipeline — they are the same I/O system that
powers print, println, and scan. Every
string operation in fast_io funnels through the library's core
I/O layer.
This means the customisation points you learned for console and file output
— print_define and print_reserve_define
— automatically make your types work with string APIs too. There is
nothing extra to implement.
concat_fast_io Uses print_reserve_define
When you call concat_fast_io, the library formats each argument
into a string by invoking the same print_reserve_define (or
print_define) that println uses. If your type is
printable to the console, it is printable into a string — for free.
For example, suppose we defined print_reserve_define for a
Point struct back in
Ch11.2:
#include <fast_io.h>
#include <fast_io_dsal/string.h>
struct Point {
double x;
double y;
};
inline constexpr void print_reserve_define(::fast_io::io_reserve_type_t<char, Point>,
char* iter,
Point const& p) {
// Format: "(x, y)"
*iter++ = '(';
iter = ::fast_io::detail::print_reserve_integral(iter, p.x);
*iter++ = ',';
*iter++ = ' ';
iter = ::fast_io::detail::print_reserve_integral(iter, p.y);
*iter++ = ')';
}
int main() {
using namespace ::fast_io::iomnp;
Point p{3.0, 4.0};
// Works on the console
println("Console: ", p);
// Also works in concat_fast_io — same customisation point
::fast_io::string s = ::fast_io::concat_fast_io("Point: ", p);
// s == "Point: (3, 4)"
println(s);
}
No separate to_string function, no
std::format specialisation, no operator<<
overload. One definition, every output target.
to<T> Uses the Same I/O Pipeline
to<T> works in two stages, both of which go through the
standard I/O pipeline:
-
Format — the arguments are formatted into a
temporary string using
concat_fast_io(and thereforeprint_reserve_define). -
Parse — the resulting string is scanned into the
target type
Tusingscan_define, the same customisation point thatscanuses.
#include <fast_io.h>
#include <fast_io_dsal/string.h>
int main() {
using namespace ::fast_io::iomnp;
// Format 42 as a string ("42"), then parse it back as a double
double d = ::fast_io::to<double>(42); // d == 42.0
// Concatenate multiple arguments, then parse the result
::std::size_t n = ::fast_io::to<::std::size_t>(1zu, 23zu);
// "1" + "23" -> "123" -> 123
println("d = ", d, ", n = ", n);
}
Because both stages reuse the standard pipeline, any custom type that
implements print_reserve_define and
scan_define can round-trip through to<T>
without any additional glue code.
concat_std: Formatting to C++ std::string
In Ch9.16, we introduced C++
Standard Library containers, including ::std::string. If you
need to interoperate with C++ Standard Library code that expects
::std::string, fast_io provides
::fast_io::concat_std — which works exactly like
concat_fast_io but produces a ::std::string
instead of a ::fast_io::string.
Just like concat_fast_io, concat_std uses the
same print_reserve_define customisation points. Any type
that works with println or concat_fast_io
automatically works with concat_std.
There are also character-type variants for UTF-8, UTF-16, UTF-32, and wide strings:
::fast_io::concat_std— produces::std::string(char)::fast_io::u8concat_std— produces::std::u8string(char8_t)::fast_io::u16concat_std— produces::std::u16string(char16_t)::fast_io::u32concat_std— produces::std::u32string(char32_t)::fast_io::wconcat_std— produces::std::wstring(wchar_t)
#include <fast_io.h>
#include <fast_io_dsal/string.h>
#include <string>
struct Point {
int x;
int y;
};
// Assume print_reserve_define is defined as in Ch11.2
int main() {
using namespace ::fast_io::iomnp;
Point p{42, 100};
// concat_std produces a ::std::string
::std::string s = ::fast_io::concat_std("Point: ", p);
// s == "Point: (42, 100)"
// u8concat_std produces a ::std::u8string
::std::u8string u8s = ::fast_io::u8concat_std(u8"Unicode: ", p);
println("std::string: ", s);
}
There is also ::fast_io::concatln_std (and its character-type
variants), which appends a newline at the end, just like
concatln_fast_io.
The primary use case for concat_std is when you need to pass
a formatted string to legacy C++ code that expects
::std::string. For new code, prefer
concat_fast_io with ::fast_io::string.
The Unified Design
The takeaway is that fast_io has one set of
customisation points for every output target:
| Customisation Point | Used By |
|---|---|
print_define / print_reserve_define |
println, print, concat_fast_io,
concatln_fast_io, concat_std,
concatln_std, to<T>
|
scan_define / scan_context_define |
scan, to<T> (parse stage) |
Console, file, buffered I/O, string, socket — they all share the same formatting and parsing layer. Define your type once and it works everywhere.
Writing to Existing Strings: {chtype}ostring_ref_fast_io
While concat_fast_io creates a new string, sometimes you want to
format output directly into an existing string buffer. The
::fast_io::{chtype}ostring_ref_fast_io family of types lets you do exactly that.
These are non-owning reference types that wrap an existing string and expose it as an output
device for print, println, and other output operations.
The {chtype} placeholder is replaced with the character type:
::fast_io::ostring_ref_fast_io— forcharstrings::fast_io::u8ostring_ref_fast_io— forchar8_t/ UTF-8 strings::fast_io::u16ostring_ref_fast_io— forchar16_t/ UTF-16 strings::fast_io::u32ostring_ref_fast_io— forchar32_t/ UTF-32 strings::fast_io::wostring_ref_fast_io— forwchar_tstrings
You construct a reference by passing a pointer to an existing string. The string is not cleared before writing — output is appended at the current position. When you are done, the string contains everything that was written.
#include <fast_io.h>
#include <fast_io_dsal/string.h>
int main()
{
using namespace ::fast_io::iomnp;
::fast_io::u8string result;
// Wrap the existing string as an output device
::fast_io::u8ostring_ref_fast_io ref(result);
// Write to the string using print/println
print(ref, "Hello, ");
print(ref, "World!");
println(ref); // adds newline
// The string now contains "Hello, World!\n"
println("Result: ", result);
// You can also write structured data
::fast_io::u8string buffer;
::fast_io::u8ostring_ref_fast_io buf_ref(buffer);
::std::size_t const x{42zu};
::std::size_t const y{100zu};
println(buf_ref, "Point: (", x, ", ", y, ")");
// buffer now contains "Point: (42, 100)\n"
println(buffer);
}
This is particularly useful when you need to build a string incrementally, or when you want to reuse a string buffer to avoid repeated allocations. The reference type itself is lightweight — it just holds a pointer to the string.
Note that these types are different from ::fast_io::u8string and similar owning
string types. The _ref_ in the name indicates that these are non-owning
references — they do not manage the lifetime of the underlying string. You must
ensure the string outlives the reference.
Key Takeaways
-
concat_fast_iois not a separate formatter — it callsprint_reserve_defineinternally, the same entry pointprintlnuses. -
to<T>formats to a string viaconcat_fast_io, then parses withscan_define. Both stages use the standard I/O pipeline. -
Any custom type that implements
print_reserve_defineautomatically works withconcat_fast_ioand the format stage ofto<T>. -
Implement both
print_reserve_defineandscan_defineand your type can round-trip throughto<T>for free. - This is the unified design: one set of customisation points for console, file, string, and socket output.
-
Use
::fast_io::{chtype}ostring_ref_fast_ioto write formatted output directly into an existing string buffer. These non-owning reference types let you build strings incrementally or reuse buffers to avoid allocations. -
Use
::fast_io::concat_stdwhen you need to produce a::std::stringfor interoperation with C++ Standard Library code. It uses the same customisation points asconcat_fast_io.