streaming redux

Vladimir Panteleev vladimir at thecybershadow.net
Tue Dec 28 03:09:09 PST 2010


On Tue, 28 Dec 2010 09:02:29 +0200, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> wrote:

> I've put together over the past days an embryonic streaming interface.  
> It separates transport from formatting, input from output, and buffered  
> from unbuffered operation.
>
> http://erdani.com/d/phobos/std_stream2.html
>
> There are a number of questions interspersed. It would be great to start  
> a discussion using that design as a baseline. Please voice any related  
> thoughts - thanks!

Here are my humble observations:

First of all: was ranges-like duck typing considered for streams? The  
language allows on-demand runtime polymorphism, and static typing allows  
compile-time detection of stream features for abstraction. Not sure how  
useful this is is practice, but it allows some optimizations (e.g. the  
code can be really fast when working with memory streams, due to inlining  
and lack of vcalls).

Also, why should there be support for unopened streams? While a stream  
should be flush-able and close-able, opening and reopening streams should  
be done at a higher level IMO.

> Question: Should we offer an open primitive at this level? If so, what  
> parameter(s) should it take?

I don't see how this would be implemented at the lowest level, taking into  
consideration all the possible stream types (network connections, pipes,  
etc.)

> Question: Should we offer a primitive rewind that takes the stream back  
> to the beginning? That might be supported even by some streams that  
> don't support general seek calls. Alternatively, some streams might  
> support seek(0, SeekAnchor.start) but not other calls to seek.

If seek support is determined at runtime by whether the call throws an  
exception or not, then I see no difference in having a rewind method or  
having non-zero seek throw.

> Question: May we eliminate seekFromCurrent and seekFromEnd and just have  
> seek with absolute positioning? I don't know of streams that allow seek  
> without allowing tell. Even if some stream doesn't, it's easy to add  
> support for tell in a wrapper. The marginal cost of calling tell is  
> small enough compared to the cost of seek.

Does anyone ever use seekFromEnd in practice (except the rare case of  
supporting certain file formats)? seekFromCurrent is a nice commodity, but  
every abstract method increases the burden for implementers.

> Buffered*Transport

I always thought that a perfect stream library would have buffering as an  
additional layer. For example: auto f = new Buffered!FileStream(...);

> abstract interface Formatter;

I'm really not sure about this interface. I can see at most three  
implementations of it (native, high-endian and low-endian variants),  
everything else being too obscure to count. I think it should be  
implemented as static structs instead. Also, having an abstract method for  
each native type is quite ugly for D standards, I'm sure there's a better  
solution.

> Question: Should all formatters require buffered transport? Otherwise  
> they might need to keep their own buffering, which ends up being less  
> efficient with buffered transports.

Ideally buffering would be optional, and constructing a buffer-enabled  
stream should be so easy it'd be an easily adoptable habit (see above).  
Last time I tried to do I/O in Java (or was it C#?) I had to instantiate  
3-4 classes before I could read from a file. D can do better.

> Question: Should we also define putln that writes the string and then an  
> line terminator?

But then you're mixing together text and binary streams into the same  
interface. I don't think this is a good idea.

> Question: Should we define a more involved protocol?

"A more involved protocol" would really be proper serialization. Calling  
toString can work as a commodity, similar to writefln's behavior.

> This final function writes a customizable "header" and a customizable  
> "footer".

What is the purpose of this? TypeInfo doesn't contain the field names, so  
it can't be used for protobuf-like serialization. Compile-time reflection  
would be much more useful.

> Question: Should we pass the size in advance, or make the stream  
> responsible for inferring it?

Code that needs to handle allocation itself can make the small effort of  
writing the lengths as well. A possible solution is to make string length  
encoding part of the interface specification, then the user can read the  
length and the contents separately themselves.

> Question: How to handle associative arrays?

Not a problem with static polymorphism.

-- 
Best regards,
  Vladimir                            mailto:vladimir at thecybershadow.net


More information about the Digitalmars-d mailing list