- By default, ::fast_io::io::print writes to C’s FILE*stdout object.
- This means the output goes to the standard output stream, just like printf or
- std::cout, but with safer and faster semantics.
-
-
- In practice, using fast_io together with stdio or iostream
- usually does not cause issues. The only problematic cases arise if code calls unusual functions
- like unput, which can break assumptions because neither stdio nor
- iostream guarantee consistent behavior for such operations. As long as you avoid
- those unsafe edge cases, interoperability is fine.
-
- In fast_io, a manipulator is a helper object that changes how
- input or output is interpreted or formatted. Instead of relying on unsafe format strings,
- manipulators make the intent explicit in the type system.
-
-
- Manipulators are defined in the namespace fast_io::manipulators, which is aliased
- as fast_io::mnp for convenience. They make input and output safer, more explicit,
- and more portable than traditional stdio or iostream.
-
-
- For example:
-
-
-
hex_get(a) — tells scan to read the variable a in hexadecimal.
-
hex(a) — tells println to print a in lowercase hexadecimal.
-
hexupper(a) — prints a in uppercase hexadecimal.
-
base_get<N>(a) — reads a in base N (2 ≤ N ≤ 36).
-
base<N>(a) — prints a in base N.
-
-
-
-
-
Hexadecimal Manipulators
-
- fast_io provides manipulators to read and print numbers in hexadecimal.
- hex_get reads input in hex, while hex and hexupper
- print output in lowercase or uppercase hex respectively:
-
- fast_io defines a rich set of manipulators to control input and output formatting.
- For convenience, the namespace fast_io::manipulators is aliased as fast_io::mnp.
-
-
-
base_get<N> — reads numbers in base N (where 2 ≤ N ≤ 36).
-
base<N> — prints numbers in base N.
-
hex — shorthand for base<16>.
-
hexupper — shorthand for base<16,true>, printing hex digits in uppercase.
-
-
- These manipulators make it easy to work with different numeric bases without relying on unsafe format strings.
-
- In fast_io, raw pointers and iterators are not printed directly. This is a deliberate
- safety choice: printing them without explicit intent can lead to undefined or inconsistent
- behavior across platforms. Instead, fast_io provides a family of pointer‑related
- manipulators that make the intent explicit and ensure consistent, portable formatting.
-
-
-
-
-
Pointer Manipulators Overview
-
-
pointervw(ptr) — prints the raw address of a pointer in fixed‑width hexadecimal.
-
os_c_str(ptr) — treats a char const* as a C‑string, using strlen or strnlen.
-
funcvw(f) — prints the address of a free function.
-
methodvw(m) — prints member function pointers, including offset information for multiple inheritance.
-
handlevw(h) — prints operating system handles, whether represented as integers (POSIX fd) or pointers (Win32 HANDLE, FILE*).
-
-
-
-
-
Printing Pointers
-
- Use pointervw to print raw addresses. The format is consistent across platforms:
- 0x followed by 8 hex digits on 32‑bit systems, or 16 hex digits on 64‑bit systems.
-
-
-#include <fast_io.h>
-
-using namespace fast_io::io;
-
-int main()
-{
- using namespace fast_io::mnp;
- int x{42};
- int *ptr{::std::addressof(x)};
- println("Address:", pointervw(ptr));
-}
-
-
-
-
-
C‑String Manipulator
-
- To treat a char const* as a C‑string, use os_c_str. This calls
- strlen or strnlen depending on overload.
-
- funcvw prints the address of a free function. methodvw prints member
- function pointers, including offset information for multiple inheritance cases.
-
- handlevw is used when printing operating system handles. A handle may be an
- integer (POSIX file descriptor) or a pointer (Win32 HANDLE, FILE*).
- handlevw adapts automatically, printing integers directly and pointers in
- consistent hexadecimal format.
-
- Pointer‑related manipulators in fast_io make printing low‑level entities explicit,
- safe, and portable. They cover raw pointers, C‑strings, free functions, member functions,
- and OS handles. This design avoids the unsafe and inconsistent behavior of
- stdio and iostream.
-
- fast_io provides file types such as ibuf_file and obuf_file
- for buffered input and output. File names can be passed as any character type, and the
- os_c_str manipulator applies here as well.
-
-
-#include <fast_io.h>
-#include <fast_io_device.h>
-
-int main()
-{
- ::fast_io::ibuf_file ibf(u8"aplusb_in.txt");
- ::fast_io::obuf_file obf(u8"aplusb_out.txt");
- ::std::size_t a, b;
- scan(ibf, a, b);
- println(obf, a + b);
-}
-
-
-
-
-
Directory Files
-
- dir_file represents a directory handle. You can open files relative to a directory
- using at(dir). This makes it easy to work with subdirectories and ensures
- portability across platforms.
-
- native_file_loader uses the operating system’s memory mapping API (if available)
- to load an entire file into memory. The file is exposed as a contiguous container, allowing
- iteration and direct access.
-
-
-#include <fast_io.h>
-
-using namespace fast_io::io;
-
-int main(int argc, char **argv)
-{
- if (argc != 2)
- {
- if (argc == 0)
- {
- return 1;
- }
- perr("Usage: ", fast_io::mnp::os_c_str(*argv), " <file>\n");
- return 1;
- }
- fast_io::native_file_loader loader(::fast_io::mnp::os_c_str(argv[1]));
- // This will load the entire file into memory through memory mapping.
- /*
- This is a contiguous container of the file.
- You can do things like:
- std::size_t sum{};
- for(auto e : loader)
- sum += e;
- */
- print(loader);
-}
-
- fast_io organizes its file types into six layers. Each layer corresponds to a
- different level of abstraction, from direct operating system calls up to C and C++ runtime
- facilities. On top of these file types, fast_io provides operations
- that unify input and output for all of them through Concepts.
-
-
-
-
-
Hierarchy of File Types
-
From bottom to top:
-
-
wine
-
nt
-
win32
-
posix
-
c
-
filebuf
-
-
-
-
-
Explainations for the Hierarchy of File Types
-
From bottom to top:
-
-
- wine —
- ::fast_io::wine_file is the lowest layer used when running Windows binaries on POSIX systems (e.g. Linux, FreeBSD) via Wine. It directly interacts with Wine’s emulated NT kernel APIs.
-
-
- nt —
- ::fast_io::nt_file accesses the Windows NT kernel directly via NtWriteFile and related syscalls. This is the most native and lowest-level file interface on modern Windows systems (NT, 2000, XP, 7, 10, etc).
-
-
- win32 —
- ::fast_io::win32_file wraps the Win32 API’s WriteFile function via kernel32.dll. It is higher-level than NT and used for compatibility with user-mode Windows applications.
-
-
- posix —
- ::fast_io::posix_file wraps POSIX-style file descriptors and maps to write(2) on Unix-like systems. On Windows, it is emulated via MSVCRT or UCRT.
-
-
- c —
- ::fast_io::c_file exposes the C standard library’s FILE* interface (e.g. fwrite(3)). It provides buffering and is widely used in legacy C code.
-
-
- filebuf —
- ::fast_io::filebuf_file exposes the C++ standard library’s std::filebuf* interface, used by std::fstream. It is the highest-level abstraction and integrates with C++ I/O streams.
-
-
-
-
-
-
Unbuffered Layers
-
- The first four layers are unbuffered. They map directly to OS APIs and do not
- add buffering:
-
-
-
::fast_io::wine_file
-
::fast_io::nt_file
-
::fast_io::win32_file
-
::fast_io::posix_file
-
-
- Each of these also has character type variants, for example:
-
-
-
::fast_io::u8nt_file
-
::fast_io::wwin32_file
-
::fast_io::u32posix_file
-
-
- These variants allow working with UTF‑8, UTF‑32, or wide character filenames depending
- on platform conventions.
-
-
-
-
-
Buffered Layers
-
- The top two layers expose existing buffered facilities provided by the C and C++ runtime
- libraries:
-
-
-
::fast_io::c_file — exposes C’s FILE* object, which already provides buffering.
-
::fast_io::filebuf_file — exposes C++ std::filebuf, which already provides buffering.
-
-
- Note: filebuf_file only has ::fast_io::filebuf_file and
- ::fast_io::wfilebuf_file. Why? Because the C++ standard library never
- implemented basic functionality for other character types such as
- ::fast_io::u8filebuf_file. Only narrow and wide character streams are
- supported.
-
-
-
-
-
Moving Between Layers
-
- You can construct higher‑level types from lower ones (through std::move), or go
- back down (through static_cast).
-
-
-// Lower to higher
-::fast_io::nt_file nfl("nt.txt", ::fast_io::open_mode::out);
-print(nfl, "Hello World from nt_file\n");
-
-::fast_io::filebuf_file fbf(std::move(nfl), ::fast_io::open_mode::out);
-print(fbf, "Hello World from filebuf_file\n");
-
-// Higher to lower
-::fast_io::filebuf_file fbf2("fb.txt", ::fast_io::open_mode::out);
-::fast_io::nt_io_observer niob{static_cast<::fast_io::nt_io_observer>(fbf2)};
-print(niob, "Hello World from nt_io_observer\n");
-
-
-
-
-
Observers
-
- Types like xxx_io_observer are simple aggregates that store a handle. They are
- trivially copyable, have no destructors, and let you safely pass handles around without
- ownership semantics.
-
-
-void foo(FILE* fp) {
- ::fast_io::c_io_observer ciob{fp};
- print(ciob, "Hello World from c_io_observer\n");
-
- ::fast_io::nt_io_observer niob{static_cast<::fast_io::nt_io_observer>(ciob)};
- print(niob, "Hello World from nt_io_observer\n");
-
- // Inspect native handle
- print(::fast_io::mnp::handlevw(niob.native_handle()));
-}
-
-
-
-
-
Native File and iobuf_file
-
- ::fast_io::native_file is an alias to the most appropriate unbuffered file type for
- the platform:
-
- My interest in building a new I/O library began in high school. While exploring
- performance benchmarks online and running my own tests, I discovered that both
- ifstream and stdio were painfully slow. In many cases,
- they were at least 5× slower, and on some platforms even
- 10× to 100× slower, compared to simply reading an entire file
- into memory and parsing it manually.
-
-
- This inefficiency convinced me that the standard I/O facilities in C++ were
- fundamentally flawed and needed a complete rethinking.
-
-
-
-
-
Generic Programming vs. OOP
-
- I was a strong admirer of the generic programming paradigm used
- in the C++ standard library’s containers and algorithms. They demonstrated how
- templates could provide both flexibility and performance. Yet, I/O in C++ felt
- inconsistent: iostream leaned heavily on object-oriented programming,
- with virtual inheritance at its core. This design choice introduced complexity
- and inefficiency, and it stood in stark contrast to the elegance of generic
- programming elsewhere in the language.
-
-
-
-
-
The Problem with Format Strings
-
- Another major frustration was the reliance on format strings.
- I always considered them redundant and unsafe. My years of programming in
- Lua, particularly while developing World of Warcraft addons,
- gave me a different perspective. Lua’s defaults often felt more correct than
- C++, though Lua itself had its own flaws.
-
-
- I came to believe that format strings should be eliminated entirely. No matter
- how you constrain them — even at compile time — they remain vulnerable. Macros,
- code generators, or even AI-generated code can reintroduce format string
- vulnerabilities. The complexity never truly disappears. In my view, format
- strings are a trillion-dollar historical mistake, comparable
- to the infamous gets() function. Since gets() was
- itself an I/O function in stdio, this only reinforced my belief
- that the entire stdio system is extremely dangerous.
-
-
-
-
-
The Birth of fast_io
-
- In 2013, when Bjarne Stroustrup introduced concepts prototypes
- at CppCon, I had a breakthrough idea: why not use concepts to rewrite the entire
- I/O subsystem? That was the moment fast_io was born — at least
- in theory. I built an early prototype using GCC’s experimental concepts
- implementation, just to prove the idea was viable.
-
-
- Unfortunately, concepts were still immature at the time, existing only as a
- Technical Specification (TS). Without proper standard library support,
- fast_io couldn’t move beyond a prototype. The real
- implementation only began in earnest around 2020, once concepts became part of
- mainstream C++.
-
-
-
-
-
Refinement and Philosophy
-
- Even after 2020, fast_io went through multiple waves of
- refactoring. My goal was not just to make it functional, but to make it
- good — elegant, portable, and robust. Because it is concepts-based,
- the library is designed to work across all platforms, and even in environments
- without operating systems.
-
-
- Herb Sutter’s talk on Herbceptions further influenced my vision.
- Since I/O is one of the largest consumers of exceptions in C++, I realized that
- fast_io would eventually need to integrate Herbceptions to
- truly complete its design. Until that happens, the library will continue to
- evolve, with APIs gradually stabilizing as the design matures.
-
-
-
-
-
Backwards Compatibility
-
- A key design principle of fast_io is backwards compatibility.
- The library is built to interoperate with multiple existing systems:
- wine (host file descriptors), NT handles,
- Win32 handles, POSIX file descriptors,
- stdio, and even filebuf from
- iostream/fstream. Considerable effort has gone into making these
- systems compatible with one another under a unified interface.
-
-
-
-
-
Freestanding Environments
-
- Because fast_io is intended to work everywhere, including
- freestanding environments, I came to realize that many features advocated by
- the C++ community — exception handling (EH), RTTI, vector,
- array, and others — simply do not function reliably outside of
- hosted environments. This reinforced the need for a library that is both
- freestanding-friendly and conceptually modern.
-
-
-
-
-
Looking Ahead
-
- fast_io is more than just an I/O library. It is a proof of
- concept — a demonstration to WG21 and the broader C++ community that I/O can
- be reimagined using modern language features. It is a statement against
- inefficiency, redundancy, and outdated design patterns. And it is a commitment
- to building tools that are both fast and conceptually clean.
-
-
-
-
Video Introduction
-
- Here’s a talk that further illustrates the ideas behind fast_io:
-