Reputation: 12502
I am trying to provide a proper API for a read-only stream class. The std::istream interface is a little too complex since it contains formatting read, while I am only interested in binary read (have to deal with compressed streams).
I came up with (requires 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;
};
The question I find difficult to answer is whether or not size()
should be part of the API. When data is read from a file on disk, I can simply use stat(2)
. When data is received via HTTP, I can simply read the value for Content-Length
(RFC 2616)...
Is this safe to requires at library level this size()
function ? Or this type of requirement should only be met at application level ?
Upvotes: 1
Views: 167
Reputation: 12502
So I ended up using:
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;
};
Technically the stream::skip
could simply by a virtual function defined as multiple calls to stream::read
but I think this is clearer this way. This leads to a concrete pipe 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;
};
Upvotes: 0
Reputation: 106196
I don't think size()
should be provided as many things that support the general concept of a stream won't be able to implement it; if you do provide it - with a sentinel value or exception when unavailable - you end up with a "fat" interface and clients that code for and test on one concrete stream implementation may start failing on another. There could also be race conditions where e.g. a file is extended or truncated between the call to size()
and later consequent read
attempts.
I'd also suggest considering size_t read_nonblocking(char*, size_t)
returning the number of characters currently available. The std::istream
interface is a reasonable place to look for ideas for other member functions.
Upvotes: 2