流接口的抽象(二进制只读输入源)
Abstraction for stream interface (binary read-only input source)
我正在尝试为只读流 class 提供适当的 API。 std::istream 接口有点太复杂,因为它包含格式化读取,而我只对二进制读取感兴趣(必须处理压缩流)。
我想到了(需要 C99 <stdint.h>
):
struct stream
{
virtual uintmax_t size() const = 0;
virtual uintmax_t tell() const = 0;
virtual bool read( char * out, size_t len ) = 0;
virtual bool skip( uintmax_t len ) = 0;
};
我觉得很难回答的问题是 size()
是否应该成为 API 的一部分。当从磁盘上的文件中读取数据时,我可以简单地使用 stat(2)
。通过 HTTP 接收数据时,我可以简单地读取 Content-Length
(RFC 2616)...
的值
在库级别要求这个 size()
函数安全吗?或者这种类型的要求只能在应用程序级别满足?
我认为 size()
不应该提供,因为许多支持流的一般概念的东西将无法实现它;如果你确实提供了它——在不可用时带有一个标记值或异常——你最终会得到一个 "fat" 接口,并且为一个具体流实现编码和测试的客户端可能会在另一个流实现上开始失败。也可能存在竞争条件,例如在调用 size()
和随后的 read
次尝试之间,文件被扩展或截断。
我还建议考虑 size_t read_nonblocking(char*, size_t)
返回当前可用的字符数。 std::istream
接口是寻找其他成员函数想法的合理位置。
所以我最终使用了:
struct stream
{
virtual intmax_t size() const { return -1; }
virtual intmax_t tell() const { return -1; }
virtual bool read( char * out, size_t len ) = 0;
virtual bool skip( intmax_t len ) = 0;
};
从技术上讲,stream::skip
可以简单地通过定义为对 stream::read
的多次调用的虚函数,但我认为这样更清楚。这导致混凝土管道 class:
struct pipe_source : public stream
{
pipe_source() {
seekable = true;
std::cin.seekg(0, std::ios::beg);
if (std::cin.fail())
{
seekable = false;
std::cin.clear();
}
}
bool read( char * out, size_t len )
{
std::cin.read( out, len );
return std::cin.good();
}
bool skip( intmax_t len )
{
if( seekable )
std::cin.seekg( len, std::ios::cur );
else
{
while( len-- > 0 )
std::cin.get();
}
return std::cin.good();
}
private:
bool seekable;
};
我正在尝试为只读流 class 提供适当的 API。 std::istream 接口有点太复杂,因为它包含格式化读取,而我只对二进制读取感兴趣(必须处理压缩流)。
我想到了(需要 C99 <stdint.h>
):
struct stream
{
virtual uintmax_t size() const = 0;
virtual uintmax_t tell() const = 0;
virtual bool read( char * out, size_t len ) = 0;
virtual bool skip( uintmax_t len ) = 0;
};
我觉得很难回答的问题是 size()
是否应该成为 API 的一部分。当从磁盘上的文件中读取数据时,我可以简单地使用 stat(2)
。通过 HTTP 接收数据时,我可以简单地读取 Content-Length
(RFC 2616)...
在库级别要求这个 size()
函数安全吗?或者这种类型的要求只能在应用程序级别满足?
我认为 size()
不应该提供,因为许多支持流的一般概念的东西将无法实现它;如果你确实提供了它——在不可用时带有一个标记值或异常——你最终会得到一个 "fat" 接口,并且为一个具体流实现编码和测试的客户端可能会在另一个流实现上开始失败。也可能存在竞争条件,例如在调用 size()
和随后的 read
次尝试之间,文件被扩展或截断。
我还建议考虑 size_t read_nonblocking(char*, size_t)
返回当前可用的字符数。 std::istream
接口是寻找其他成员函数想法的合理位置。
所以我最终使用了:
struct stream
{
virtual intmax_t size() const { return -1; }
virtual intmax_t tell() const { return -1; }
virtual bool read( char * out, size_t len ) = 0;
virtual bool skip( intmax_t len ) = 0;
};
从技术上讲,stream::skip
可以简单地通过定义为对 stream::read
的多次调用的虚函数,但我认为这样更清楚。这导致混凝土管道 class:
struct pipe_source : public stream
{
pipe_source() {
seekable = true;
std::cin.seekg(0, std::ios::beg);
if (std::cin.fail())
{
seekable = false;
std::cin.clear();
}
}
bool read( char * out, size_t len )
{
std::cin.read( out, len );
return std::cin.good();
}
bool skip( intmax_t len )
{
if( seekable )
std::cin.seekg( len, std::ios::cur );
else
{
while( len-- > 0 )
std::cin.get();
}
return std::cin.good();
}
private:
bool seekable;
};