原文
C++
中文件操作
这一块比较混乱,可用std::fstream
去读写文件,也可用fread/fwrite
去读写文件,linux
平台上还可用pread/pwrite
去读写文件.
除此外,linux
平台还可io_uring
去读写文件,窗口
平台则iocp
去读写文件.
这些文件操作的api
各不相同,有时不知道选择哪个接口
更好,如果有一个统一的文件操作接口
那就好了,统一的api
去操作文件,操作方式
包括上述这些读写方式
.
如何设计一个统一的文件操作库
文件操作
从读写方式
上分类可分为两类,一个是顺序读写
,一个是随机读写
,清理一下这些文件操作方式
:
文件顺序读写
:
fstream.read/write;
fread/fwrite;
linux,io_uring
顺序读写;
窗口
,iocp
顺序读写
文件随机读写
:
windows ReadFile/WriteFile;
linux pread/pwrite;
linux,io_uring
随机读写;
窗口
,iocp
随机读写
再从另外一个维度
分类,同步读写和异步读写
:
同步读写
:
fstream.read/write;
2.fread/fwrite;
windows ReadFile/WriteFile;
2.linux pread/pwrite;
异步读写
:
linux,io_uring
读写;
窗口
,iocp
读写
要统一
起来似乎有点麻烦.可这样分类
,无论是异步
还是同步
,读写本质都从属于顺序读写
或随机读写
,因此可这样抽象:
将文件
分为两类,一类是顺序读写
的文件,另外一类是随机读写
的文件,这两类文件
都支持同步读写和异步读写
.
但是,还有个问题,文件同步操作
会阻塞当前线程
,很多时候不想阻塞
,此时可把同步读写文件
的任务交给线程池
,并提供一个异步读写接口
,在C++20
中当然是提供协程接口
了.
io_uring
和iocp
是提供真正
异步文件接口的本地
异步,而fstream,fread,pread
这些同步接口
需要放到线程池
中读写文件.
因此可提供一个统一
的协程读写接口
,默认
是会使用io_uring/iocp
去读写文件
,也允许用户显式
指定线程池
方式读写文件
.
最终统一
的跨平台的文件操作库
如下:
enum class execution_type { none, native_async, thread_pool };
template <execution_type execute_type = execution_type::native_async>
class basic_seq_coro_file {
public:
async_simple::coro::Lazy<std::pair<std::error_code, size_t>> async_read(
char *buf, size_t size);
async_simple::coro::Lazy<std::pair<std::error_code, size_t>> async_write(
std::string_view buf);
};
对顺序文件
读写来说,用户只需要定义basic_seq_coro_file
时,指定execution_type
就可以了,默认为native_async
,会通过io_uring
或iocp
异步写文件,如果设置native_async
为thread_pool
,则会在线程池
中通过std::fstream
顺序读写文件
.
再来看看随机读写文件
的类,也是类似的,只是多了一个偏移
:
template <execution_type execute_type = execution_type::native_async>
class basic_random_coro_file {
public:
async_simple::coro::Lazy<std::pair<std::error_code, size_t>> async_read_at(
uint64_t offset, char *buf, size_t size);
async_simple::coro::Lazy<std::pair<std::error_code, size_t>> async_write_at(
uint64_t offset, std::string_view buf);
};
默认io_uring/iocp
异步读写文件,如果指定execute_type
为thread_pool
方式,则会在线程池
中pread/ReadFile,pwrite/WriteFile
去随机读写文件
.
如果嫌模板类
定义太长了,可给个默认别名
:
using coro_file = basic_seq_coro_file<>;
using random_coro_file = basic_random_coro_file<>;
这样用起来
就很方便了
:
coro_file
顺序读写文件
async_simple::coro::Lazy<void> seq_read(std::string filename) {
coro_io::coro_file file{};
file.open(filename, std::ios::in);
CHECK(file.is_open());
std::string str;
str.resize(200);
{
auto pair = co_await file.async_read(str.data(), 10);
CHECK(pair.second == 10);
CHECK(!file.eof());
}
{
bool ok = file.seek(10, std::ios::beg);
CHECK(ok);
}
{
auto pair = co_await file.async_read(str.data(), str.size());
CHECK(pair.second == 5);
CHECK(file.eof());
}
}
async_simple::coro::Lazy<void> seq_write(std::string filename) {
coro_io::coro_file file{};
//创建一个可读可写的新文件
file.open(filename, std::ios::in | std::ios::out | std::ios::trunc);
CHECK(file.is_open());
std::string str = "hello";
co_await file.async_write(str);
std::string result;
result.resize(10);
CHECK(file.seek(0, std::ios::beg));
auto [rd_ec, size] = co_await file.async_read(result.data(), 5);
std::string_view s(result.data(), size);
CHECK(s == "hello");
}
这样默认就会通过io_uring/iocp
异步读写文件,如果想在线程池
中读写,则这样调用
:
void test_seq_read_write(std::string_view filename) {
create_files({std::string(filename)}, 190);
coro_io::basic_seq_coro_file<coro_io::execution_type::thread_pool> file(filename, std::ios::in | std::ios::out);
CHECK(file.is_open());
char buf[100];
std::error_code ec;
size_t size;
std::tie(ec, size) = async_simple::coro::syncAwait(file.async_read(buf, 10));
CHECK(size == 10);
std::string str = "test";
std::tie(ec, size) = async_simple::coro::syncAwait(file.async_write(str));
CHECK(size == 4);
}
接口简单易用,open
接口和std::fstream
打开方式一样,seek
参数也是一样,免去了重新学习
新api
的负担.
coro_file
随机读写文件
void test_random_read_write(std::string_view filename) {
create_files({std::string(filename)}, 190);
random_coro_fil file(filename, std::ios::in);
CHECK(file.is_open());
char buf[100];
auto pair = async_simple::coro::syncAwait(file.async_read_at(0, buf, 10));
CHECK(std::string_view(buf, pair.second) == "AAAAAAAAAA");
CHECK(!file.eof());
coro_io::basic_random_coro_file<execute_type> file1;
file1.open(filename, std::ios::out);
CHECK(file1.is_open());
std::string buf1 = "cccccccccc";
async_simple::coro::syncAwait(file1.async_write_at(0, buf1));
std::string buf2 = "dddddddddd";
async_simple::coro::syncAwait(file1.async_write_at(10, buf2));
}
coro_file
在哪里?它就是yalantinglibs.coro_file
:这里