第26.5章:设备

English

1. 设备是什么

设备是 I/O 操作的端点。它不是格式化器,也不是容器。这个概念由类型提供的操作定义:

能力 操作 例子
输入 read_some、read_all、pread_*、 scatter 变体 native_file、ibuf_file、iobuf_file、 iobuf_pipe、socket 文件
输出 write_some、write_all、pwrite_*、 scatter 变体 native_file、obuf_file、iobuf_file、 控制台 observer
定位 位置查询与 seek 文件;管道和 socket 不支持
传输 transmit_some_bytes、transmit_all_bytes 带 transmit handle 的文件、管道和 socket
缓冲 basic_ibuf、basic_obuf、basic_iobuf 包在设备外面的带缓冲包装

一个类型不必实现所有能力。socket 不能 seek;管道是单向的;控制台不是普通文件。 类型系统会在你请求缺失能力的地方报错,而不是假装每个设备都是行为一致的文件描述符。

2. 拥有设备的类型 vs 观察者

fast_io 把“拥有句柄的东西”和“仅仅引用句柄的东西”分开。 拥有类型在析构时关闭句柄;观察者类型不会。

角色 例子 生命周期规则
拥有者 native_file、ibuf_file、obuf_file、 iobuf_file、iobuf_pipe、iobuf_socket_file、 c_file、filebuf_file 析构时关闭;只能移动。
观察者 native_io_observer、c_io_observer、 filebuf_io_observer、in()、out()、 err() 的结果 借用;不能比拥有者活得更久。
缓冲包装 basic_ibuf、basic_obuf、basic_iobuf、 ibuf_file、obuf_file、iobuf_file 拥有底层设备并增加缓冲。

函数返回观察者,是 fast_io 在不转移所有权的情况下暴露设备的方式; 返回拥有者,是工厂把一个你必须关闭的资源交给你。

3. 文件家族

类型 缓冲 方向 用途
::fast_io::native_file 不带缓冲 双向、可定位 大块传输、mmap、直接 OS 语义。
::fast_io::ibuf_file 带缓冲 输入 逐行/逐 token 扫描、小读。
::fast_io::obuf_file 带缓冲 输出 大量小写、格式化输出。
::fast_io::iobuf_file 带缓冲 双向 同一个文件读写、修补头部。
::fast_io::ibuf_file_lockable 等 带缓冲 + 互斥 同基础类型 跨线程共享一个流。
::fast_io::c_file 遗留 FILE* 双向 C 库互操作;新代码只读。
::fast_io::filebuf_file 遗留 basic_filebuf 双向 iostream 互操作;新代码只读。

带缓冲文件类型有五种字符变体:char、char8_t、 char16_t、char32_t 和 wchar_t。 原生文件类型面向字节;带缓冲包装携带 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. 缓冲是一层包装

basic_ibuf、basic_obuf 和 basic_iobuf 是通用包装。命名的文件类型是“原生文件 + 包装 + 选定字符类型”的别名。 所以同一套缓冲规则适用于文件、管道和 socket:

包装 增加什么 典型别名
basic_ibuf<T> 输入缓冲与重新填充 ibuf_file
basic_obuf<T> 输出缓冲与刷新 obuf_file
basic_iobuf<T> 两侧缓冲 iobuf_file、iobuf_pipe、 iobuf_socket_file

包装仍然是设备。它满足和底层类型相同的输入/输出流概念,外加缓冲操作。 这就是为什么 scan 可以要求带缓冲输入并接受 ibuf_file, 却拒绝不带缓冲的 native_file。

5. 管道、socket 与控制台

设备 模块 说明
native_pipe 头文件侧 不带缓冲的句柄对;有 in() 和 out() 两端。 目前不在模块导出列表里。
iobuf_pipe fast_io 带缓冲管道,用于 print/scan 和进程输出。
native_socket_file fast_io 原始 socket 句柄;方向取决于协议。
iobuf_socket_file fast_io 带缓冲 socket,用于格式化协议。
tls_socket、iobuf_tls_socket_file fast_io.tls socket 上面的 TLS 层;I/O 操作相同。
in()、out()、err() fast_io 原生标准流上的 observer。
dir_file fast_io 目录句柄;可以迭代,或相对它打开文件。

管道与进程见 第11.20章;socket 见 第11.10章;TLS 见 fast_io.tls 模块。

6. 选择设备

  1. 需要格式化文本? 用 ibuf_file / obuf_file / iobuf_file。如果还要适配非文件流, 用对应的带缓冲管道或 socket 类型。
  2. 搬大块数据? 用 native_file 和 transmit,或者 第11.8章 的内存映射。
  3. 需要一个借用的句柄? 用 observer 类型,不要用拥有者。
  4. 需要跨线程共享一个流? 用可加锁的带缓冲别名, 不要用普通缓冲加外部锁。
  5. 在读 C 或 iostream 代码? 在边界处用 c_file 或 filebuf_file;不要在新代码上继续搭。

7. 模块导出了什么

fast_io 模块导出文件家族、管道家族、socket 文件、 dir_file、缓冲包装和可加锁别名。c_file 和 filebuf_file 来自 fast_io_legacy。TLS socket 类型来自 fast_io.tls。不在 device.inc 或 io_buffer.inc 里的头文件设备辅助,不属于模块表面。

所有权规则。 如果类型有会关闭句柄的析构函数,它就是拥有者。 如果它由 in()、out()、err() 或 *_io_observer 产生,它就是借用。不要把借用保存到拥有者生命周期之后。

要点