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