1

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 ?

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

std::istream::tellg is actually pretty dumb as an API.
std::istream::seekg is even worse since connected to std::cin you have no clue whether this is working or not, eg: ./a.out < bla.cxx vs cat bla.cxx | ./a.out. clearly std::istream API is not that great.
0

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; }; 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.