第26.5章:设备
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 |
目录句柄;可以迭代,或相对它打开文件。 |
6. 选择设备
- 需要格式化文本? 用
ibuf_file/obuf_file/iobuf_file。如果还要适配非文件流, 用对应的带缓冲管道或 socket 类型。 - 搬大块数据? 用
native_file和transmit,或者 第11.8章 的内存映射。 - 需要一个借用的句柄? 用 observer 类型,不要用拥有者。
- 需要跨线程共享一个流? 用可加锁的带缓冲别名, 不要用普通缓冲加外部锁。
- 在读 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 产生,它就是借用。不要把借用保存到拥有者生命周期之后。
要点
- 设备由能力定义:输入、输出、定位、传输和缓冲。
- 拥有者在析构时关闭且只能移动;观察者借用句柄。
native_file不带缓冲;ibuf_file、obuf_file和iobuf_file增加缓冲。basic_ibuf、basic_obuf和basic_iobuf是命名别名背后的通用包装。- 管道、socket 和 TLS socket 都是同一套操作下的设备,不是另一个 API 宇宙。
c_file和filebuf_file是遗留互操作类型;fast_io_legacy导出它们。- 按访问模式选择缓冲,不要只看文件大小。