malat
malat

Reputation: 12502

Abstraction for stream interface (binary read-only input source)

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

Answers (2)

malat
malat

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

Tony Delroy
Tony Delroy

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

Related Questions