Ch26.5: Devices

中文(简体)

1. What a Device Is

A device is the endpoint of an I/O operation. It is not a formatter and not a container. The concept is defined by the operations a type provides:

Capability Operation Examples
Input read_some, read_all, pread_*, scatter variants native_file, ibuf_file, iobuf_file, iobuf_pipe, socket files
Output write_some, write_all, pwrite_*, scatter variants native_file, obuf_file, iobuf_file, console observers
Seek position queries and seeks files; not pipes or sockets
Transmit transmit_some_bytes, transmit_all_bytes files, pipes and sockets with a transmit handle
Buffer basic_ibuf, basic_obuf, basic_iobuf buffered wrappers around a device

A type does not have to implement all capabilities. A socket is not seekable. A pipe is unidirectional. A console is not a regular file. The type system reports the missing capability at the point where you ask for it, instead of pretending every device is a file descriptor with the same behaviour.

2. Owning Device vs Observer

fast_io separates the thing that owns a handle from the thing that merely refers to one. The owning type closes the handle in its destructor; the observer type does not.

Role Examples Lifetime rule
Owner native_file, ibuf_file, obuf_file, iobuf_file, iobuf_pipe, iobuf_socket_file, c_file, filebuf_file Closes on destruction; move-only.
Observer native_io_observer, c_io_observer, filebuf_io_observer, the result of in(), out(), err() Borrowed; must not outlive the owner.
Buffer wrapper basic_ibuf, basic_obuf, basic_iobuf, ibuf_file, obuf_file, iobuf_file Owns the underlying device and adds buffering.

Returning an observer from a function is how fast_io exposes a device without transferring ownership. Returning an owner is how a factory hands you a resource you must close.

3. The File Family

Type Buffering Direction Use
::fast_io::native_file Unbuffered Bidirectional, seekable Large bulk transfers, mmap, direct OS semantics.
::fast_io::ibuf_file Buffered Input Line/token scanning, small reads.
::fast_io::obuf_file Buffered Output Many small writes, formatted output.
::fast_io::iobuf_file Buffered Bidirectional Read/write in one file, patching headers.
::fast_io::ibuf_file_lockable etc. Buffered + mutex Same as base One shared stream across threads.
::fast_io::c_file Legacy FILE* Bidirectional C library interoperation; read-only for new code.
::fast_io::filebuf_file Legacy basic_filebuf Bidirectional iostream interoperation; read-only for new code.

The five character variants exist for the buffered file types: char, char8_t, char16_t, char32_t and wchar_t. The native file type is byte-oriented; the buffered wrappers carry the character type used by print/scan.


#include <fast_io.h>

int main()
{
    using namespace ::fast_io::io;

    ::fast_io::obuf_file out("out.txt");
    println(out, "value = ", 42);

    ::fast_io::ibuf_file in("out.txt");
    ::fast_io::string line;
    scan(in, ::fast_io::mnp::strlike_line_get(line));
}

4. Buffering Is a Wrapper

basic_ibuf, basic_obuf and basic_iobuf are the generic wrappers. The named file types are aliases that combine a native file with the wrapper and choose a character type. This is why the same buffering rules apply to files, pipes and sockets:

Wrapper Adds Typical alias
basic_ibuf<T> Input buffer and refill ibuf_file
basic_obuf<T> Output buffer and flush obuf_file
basic_iobuf<T> Both buffers iobuf_file, iobuf_pipe, iobuf_socket_file

A wrapper is still a device. It satisfies the same input/output stream concepts as the underlying type, plus buffer operations. That is why scan can require a buffered input and accept ibuf_file while rejecting an unbuffered native_file.

5. Pipes, Sockets and Consoles

Device Module Notes
native_pipe header-side Unbuffered pair; in() and out() ends. Not in the module export list yet.
iobuf_pipe fast_io Buffered pipe for print/scan and process output.
native_socket_file fast_io Raw socket handle; unidirectional or bidirectional depending on protocol.
iobuf_socket_file fast_io Buffered socket for formatted protocols.
tls_socket, iobuf_tls_socket_file fast_io.tls TLS layer over a socket; same I/O operations.
in(), out(), err() fast_io Observers over the native standard streams.
dir_file fast_io Directory handle; iterate or open files relative to it.

Pipes and processes are covered in Ch11.20; sockets in Ch11.10; TLS in the fast_io.tls module.

6. Choosing a Device

  1. Do you need formatted text? Use ibuf_file / obuf_file / iobuf_file. If you also need portability to a non-file stream, use the matching pipe or socket buffered type.
  2. Are you moving a large blob? Use native_file and transmit, or a memory map from Ch11.8.
  3. Do you need a borrowed handle? Use the observer type, not the owner.
  4. Do you need one shared stream across threads? Use the lockable buffered alias, not a plain buffer plus an external lock.
  5. Are you reading C or iostream code? Use c_file or filebuf_file at the boundary; do not build new code on them.

7. What the Module Exports

The fast_io module exports the file family, the pipe family, socket files, dir_file, the buffer wrappers and the lockable aliases. c_file and filebuf_file come from fast_io_legacy. TLS socket types come from fast_io.tls. Header-only device helpers that are not in device.inc or io_buffer.inc are not part of the module surface.

Ownership rule. If a type has a destructor that closes a handle, it is an owner. If it is produced by in(), out(), err() or a *_io_observer, it is a borrow. Do not store a borrow past the lifetime of the owner.

Key Takeaways