Ch26.2: print
1. The Function Surface
The print family is deliberately small. Every function returns void and
reports failure through herbceptions. The difference between the names is the
destination, the newline, and whether the call is allowed to return.
| Function | Default destination | Newline | Availability |
|---|---|---|---|
print(args...) |
C stdout |
No | Always |
println(args...) |
C stdout |
Yes, \n |
Always |
perr(args...) |
native stderr |
No | Always |
perrln(args...) |
native stderr |
Yes, \n |
Always |
panic(args...) |
native stderr |
No | Always; terminates |
panicln(args...) |
native stderr |
Yes, \n |
Always; terminates |
debug_print(args...) |
native stdout |
No | Only when NDEBUG is not defined |
debug_println(args...) |
native stdout |
Yes | Only when NDEBUG is not defined |
debug_perr(args...) |
native stderr |
No | Only when NDEBUG is not defined |
debug_perrln(args...) |
native stderr |
Yes | Only when NDEBUG is not defined |
Every function also accepts an output device as its first argument:
print(device, args...), println(device, args...),
perr(device, args...), and so on. When the first argument is not an output
device, the default destination in the table is used.
perr writes to the platform's native standard error, not to C's
stderr. It is unbuffered and each call is a syscall, which gives the
atomicity you want for diagnostics. panic and panicln are the
same destination followed by a trap: they do not return.
2. What Counts as the First Argument
The first argument is a device if it satisfies the output-stream or I/O-stream concept used by fast_io. Otherwise it is printed as a value and the default destination is used. This is why both of these are valid:
#include <fast_io.h>
int main()
{
using namespace ::fast_io::io;
// Value first: default destination.
println("answer = ", 42);
// Device first: explicit destination.
::fast_io::obuf_file out("out.txt");
println(out, "answer = ", 42);
}
There is no format string and no per-call state. The device is chosen once, and every
remaining argument is formatted according to its own type and any manipulator wrapped
around it. This is why print composes without the iostream state machine:
there is nothing to restore after the call.
3. Formatting Is Argument-Local
A manipulator is a value, not a stream flag. mnp::hex, mnp::fixed,
mnp::left and the rest wrap exactly one argument:
println("decimal: ", 255);
println("hex: ", ::fast_io::mnp::hex(255));
println("padded: ", ::fast_io::mnp::right(42, 8, '0'));
The next statement is unaffected. There is no equivalent of forgetting to reset
std::hex, and no equivalent of a manipulator leaking into a different
stream. The complete exported list, including base, floating, width, pointer, string,
binary, network and locale manipulators, is in
Ch26.1.
Custom types join the same path through print_reserve_define or
print_define. The print function does not need to know the type; it asks the
type's formatter to write into the output device. A type that can only be printed through
a scatter of existing bytes should use print_scatter_define.
4. Return Value and Failure
The print family has no return value. A failure is thrown as a herbception, so the normal handler is the same one used elsewhere in this book:
#include <fast_io.h>
int main()
{
try
{
::fast_io::io::println(::fast_io::out(), "hello");
}
catch throws(::std::error e)
{
::fast_io::io::perrln(e);
return 1;
}
}
Formatting a type that has no formatter is a compile-time error, not a runtime one. A runtime failure comes from the device layer: a closed file, a full disk, a broken pipe, or an interrupted syscall.
5. Buffering, Flushing and Synchronization
print works with both buffered and unbuffered output devices. A buffered
device batches small writes and flushes when the buffer is full or when the object is
destroyed; an unbuffered device writes through. This is the same split described in
Ch11.5, and it is why the print family does not
force every caller to choose a buffer size.
perr is intentionally unbuffered. Diagnostics must arrive even if the
process is about to trap or another thread is still holding a buffered stream. The
price is one syscall per call; do not use perr in a hot loop.
Thread safety depends on the device. A mutex-wrapped device (for example a lockable file
type) serializes calls. A plain buffered file does not become thread-safe because the
function is named println. Process safety is a stronger property: the
print family guarantees it only when the device is not a buffered output stream.
6. Debug Output
The debug_* family exists so that diagnostic prints can disappear from
release builds. When NDEBUG is defined, these functions are not available;
code that still references them fails to compile instead of silently producing output.
Their default destination is the native standard stream, not C's stream, so they still
work when the C runtime is not in a useful state.
debug_println("state = ", state);
Do not use debug_print as a logging API. It is a development tool, and its
availability is intentionally tied to the build mode.
7. What to Use for What
| Need | Use |
|---|---|
| Normal program output | print / println to the default or an explicit file/device |
| Diagnostics that must survive a crash | perr / perrln |
| Impossible state | panic / panicln |
| Development-only tracing | debug_print / debug_perr |
| Bytes, not values | transmit (see Ch11.5) |
| Locale-aware numbers or time | imbue(loc, out) (see Ch26.1) |
Key Takeaways
- The print family is
print,println,perr,perrln,panic,paniclnand thedebug_*variants. print/printlndefault to Cstdout;perr/perrlnuse native unbufferedstderr.- A device can be the first argument; otherwise the default destination is used.
- Manipulators affect one argument only; there is no stream state to reset.
- All functions return
voidand report device failures as herbceptions. panicterminates;debug_*disappears underNDEBUG.- Use
transmitfor byte movement, not the print family.