www.digitalmars.com         C & C++   DMDScript  

D - Interface for all Interested:

reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
This, IMHO, would be a good family of standard interfaces for input
streams.  Thoughts?

BTW, I intentionally designed InStream such that it would be very easy
to overload the array slicing operator....



interface InStream
{
public:
    class StreamDataExpired_Error : public Error {};

public:
    char[] Get(ulong startIndex,ulong endIndexPlusOne);
            // note that the indices here follow the [start..end+1]
convention of array slicing
            // Get() may throw StreamDataExpired_Error if you attempt to
re-read an index
            //        you've read before.  Some classes, including the
BufferingInStream interface,
            //        have the ability to buffer old data and so might
not do that.
            // Get() will block until all the data up to endIndex is
ready.
            // Get() will return a shorter array if you hit EOF.  Check
retval.length to see if this
            //         happened.
    char[] Get(ulong startIndex,ulong endIndexPlusOne,ulong
minEndIndexPlusOne);
            // this is like Get(), but will only block to satisfy the
minimum.  It will never return
            //        more than endIndex.  Like Get(), it may throw
StreamDataExpired_Error.
};

interface BufferedInStream : InStream
{
    // this interface is like InStream, but it buffers all data,
allowing you to view it multiple times.
    // This interface is most useful when the stream you're modeling is
a mmap'd file or something
    // like that that will be static anyway.

public:
    void ExpireDataUpTo(ulong endIndexPlusOne);
        // this tells the class that it can discard old buffered data up
to this index.  It may ignore you,
        // so there is no guarantee that a later call to
Get(<expiredData>) will throw an error (unless
        // the class makes that guarantee)
};

--
The Villagers are Online! http://villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]
Mar 22 2003
parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
It kind of seems nice, but there's always something extra that someone deems
essential, that "your" interface has omitted and "theirs" has got. And vice
versa.

For instance, your InStream deals in char[]. What if the data is binary?
Also, expressing the buffering in an interface seems very strange. One would
usually imagine that buffering be a characteristic of one concrete
interface-implementer vs another.

I actually think that a rationalised and D-ised version of the COM stream /
sequential-stream interfaces would be the way to go. The interface deals in
binary stuff only, and different implementing classes can overlay
characteristics such as bin-text conversion (cr-lf issues). Perhaps - and
this is just off the top of the head - it could look like this

interface SequentialInStream
{
  Read . . . // precise signature to be decided, but this would read arrays
of bytes
}

interface SequentialOutStream
{
  Write . . . // precise signature to be decided, but this would write
arrays of bytes
}

interface InStream
{
  Seek . . . // tbd
  Clone . . . // tbd
  other methods tbd
}

interface OutStream
{
  Seek . . . // tbd
  Clone . . . // tbd
  other methods tbd
}

Everything that operates on types could be layered on top of this, in
implementing classes.

I have a moderately elegant C++ template version of object instance
streaming that is syntactically similar to MFC's UpdateData data exchange
thingy, in the sense that the in and out code is written once. The
difference is that everything is resolved at compile time, and it's
exceedingly quick. It layers stuff over a binary stream interface, and could
maybe be converted into D. I'm not yet sufficiently up to speed on D's
templates to say yeah or nay.

The point, however, is that I think the concrete aspects of streaming should
be as generic as poss - binary interface - to reduce feature creep which
limits the number of possible interface-implementing types, and the
object-streaming should be thin as possible - template stuff - to reduce
code size and keep efficiency high. This basically answers the two main
problems with C++'s iostreams.

That all sounds super cool, but of course the real world impacts in what, if
anything, comes between these two layers. This includes character string
streaming, exception-handling, dealing with cr-lf, etc. etc. If this
middle-layer can be sorted such that it is straightforward (in terms of
normal operation and error handling), efficient, extensible by the
programmer without needing much extension in the library, then I think we'd
have something rather fantastic.

Even though I am first and foremost a C++ fan, I've hated the iostreams for
a _long_ time, and it would tickle me no end if we could come up with a
Dstreams architecture that answered all the iostreams faults with elegance.
Trumpeting that on the Boost newsgroup would give D a massive fillip. ;)

Matthew

"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3E7C970C.52EE26CD deming-os.org...
 This, IMHO, would be a good family of standard interfaces for input
 streams.  Thoughts?

 BTW, I intentionally designed InStream such that it would be very easy
 to overload the array slicing operator....



 interface InStream
 {
 public:
     class StreamDataExpired_Error : public Error {};

 public:
     char[] Get(ulong startIndex,ulong endIndexPlusOne);
             // note that the indices here follow the [start..end+1]
 convention of array slicing
             // Get() may throw StreamDataExpired_Error if you attempt to
 re-read an index
             //        you've read before.  Some classes, including the
 BufferingInStream interface,
             //        have the ability to buffer old data and so might
 not do that.
             // Get() will block until all the data up to endIndex is
 ready.
             // Get() will return a shorter array if you hit EOF.  Check
 retval.length to see if this
             //         happened.
     char[] Get(ulong startIndex,ulong endIndexPlusOne,ulong
 minEndIndexPlusOne);
             // this is like Get(), but will only block to satisfy the
 minimum.  It will never return
             //        more than endIndex.  Like Get(), it may throw
 StreamDataExpired_Error.
 };

 interface BufferedInStream : InStream
 {
     // this interface is like InStream, but it buffers all data,
 allowing you to view it multiple times.
     // This interface is most useful when the stream you're modeling is
 a mmap'd file or something
     // like that that will be static anyway.

 public:
     void ExpireDataUpTo(ulong endIndexPlusOne);
         // this tells the class that it can discard old buffered data up
 to this index.  It may ignore you,
         // so there is no guarantee that a later call to
 Get(<expiredData>) will throw an error (unless
         // the class makes that guarantee)
 };

 --
 The Villagers are Online! http://villagersonline.com

 .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
 .[ (a version.of(English).(precise.more)) is(possible) ]
 ?[ you want.to(help(develop(it))) ]

Mar 22 2003
parent reply "Jon Allen" <jallen minotstateu.edu> writes:
I like it, but I too have a couple suggestions.  In C++ I would use char*
(for binary or text, or anything else). However, Walter was kind enough to
provide us with a byte[] type which I'm assuming is for those kinds of
ambiguities.

I also think that maybe a buffered interface isn't completely necessary.

As far as seeking and a start index, well they are a great idea for streams
like files and the like, but what about streeaming data from sockets,
keyboards, or anything else that is a little more dynamic?  I suggest
leaving off the start index and just using a length and minlength.  Seeks
could be implemented in classes where they make sense, instead of being part
of the interface.  Seek functions would of course be used to replace the
start index parameter.  You might lose a little speed that way, but I think
it'd be worth it.

I also think a this(InStream) constructor would be a little more consistant
than a clone function, but I think either one could be annoying in an
interface.

"Matthew Wilson" <dmd synesis.com.au> wrote in message
news:b5igjc$301c$1 digitaldaemon.com...
 It kind of seems nice, but there's always something extra that someone

 essential, that "your" interface has omitted and "theirs" has got. And

 versa.

 For instance, your InStream deals in char[]. What if the data is binary?
 Also, expressing the buffering in an interface seems very strange. One

 usually imagine that buffering be a characteristic of one concrete
 interface-implementer vs another.

 I actually think that a rationalised and D-ised version of the COM stream

 sequential-stream interfaces would be the way to go. The interface deals

 binary stuff only, and different implementing classes can overlay
 characteristics such as bin-text conversion (cr-lf issues). Perhaps - and
 this is just off the top of the head - it could look like this

 interface SequentialInStream
 {
   Read . . . // precise signature to be decided, but this would read

 of bytes
 }

 interface SequentialOutStream
 {
   Write . . . // precise signature to be decided, but this would write
 arrays of bytes
 }

 interface InStream
 {
   Seek . . . // tbd
   Clone . . . // tbd
   other methods tbd
 }

 interface OutStream
 {
   Seek . . . // tbd
   Clone . . . // tbd
   other methods tbd
 }

 Everything that operates on types could be layered on top of this, in
 implementing classes.

 I have a moderately elegant C++ template version of object instance
 streaming that is syntactically similar to MFC's UpdateData data exchange
 thingy, in the sense that the in and out code is written once. The
 difference is that everything is resolved at compile time, and it's
 exceedingly quick. It layers stuff over a binary stream interface, and

 maybe be converted into D. I'm not yet sufficiently up to speed on D's
 templates to say yeah or nay.

 The point, however, is that I think the concrete aspects of streaming

 be as generic as poss - binary interface - to reduce feature creep which
 limits the number of possible interface-implementing types, and the
 object-streaming should be thin as possible - template stuff - to reduce
 code size and keep efficiency high. This basically answers the two main
 problems with C++'s iostreams.

 That all sounds super cool, but of course the real world impacts in what,

 anything, comes between these two layers. This includes character string
 streaming, exception-handling, dealing with cr-lf, etc. etc. If this
 middle-layer can be sorted such that it is straightforward (in terms of
 normal operation and error handling), efficient, extensible by the
 programmer without needing much extension in the library, then I think

 have something rather fantastic.

 Even though I am first and foremost a C++ fan, I've hated the iostreams

 a _long_ time, and it would tickle me no end if we could come up with a
 Dstreams architecture that answered all the iostreams faults with

 Trumpeting that on the Boost newsgroup would give D a massive fillip. ;)

 Matthew

 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3E7C970C.52EE26CD deming-os.org...
 This, IMHO, would be a good family of standard interfaces for input
 streams.  Thoughts?

 BTW, I intentionally designed InStream such that it would be very easy
 to overload the array slicing operator....



 interface InStream
 {
 public:
     class StreamDataExpired_Error : public Error {};

 public:
     char[] Get(ulong startIndex,ulong endIndexPlusOne);
             // note that the indices here follow the [start..end+1]
 convention of array slicing
             // Get() may throw StreamDataExpired_Error if you attempt to
 re-read an index
             //        you've read before.  Some classes, including the
 BufferingInStream interface,
             //        have the ability to buffer old data and so might
 not do that.
             // Get() will block until all the data up to endIndex is
 ready.
             // Get() will return a shorter array if you hit EOF.  Check
 retval.length to see if this
             //         happened.
     char[] Get(ulong startIndex,ulong endIndexPlusOne,ulong
 minEndIndexPlusOne);
             // this is like Get(), but will only block to satisfy the
 minimum.  It will never return
             //        more than endIndex.  Like Get(), it may throw
 StreamDataExpired_Error.
 };

 interface BufferedInStream : InStream
 {
     // this interface is like InStream, but it buffers all data,
 allowing you to view it multiple times.
     // This interface is most useful when the stream you're modeling is
 a mmap'd file or something
     // like that that will be static anyway.

 public:
     void ExpireDataUpTo(ulong endIndexPlusOne);
         // this tells the class that it can discard old buffered data up
 to this index.  It may ignore you,
         // so there is no guarantee that a later call to
 Get(<expiredData>) will throw an error (unless
         // the class makes that guarantee)
 };

 --
 The Villagers are Online! http://villagersonline.com

 .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
 .[ (a version.of(English).(precise.more)) is(possible) ]
 ?[ you want.to(help(develop(it))) ]


Mar 22 2003
parent reply "Matthew Wilson" <dmd synesis.com.au> writes:
Cool.

A couple of clarifications:

- I did mention bytes in the interface method descriptions, and that would
indeed be the recommended interchange type (at least in the interfaces. As I
said, the implementing classes would provide suitable overloads)
- I forgot to show the inheritance relationship of InStream <-
SequentialInStream, and OutString <- SequentialOutStream

I don't get your point about seeking, indexes, sockets, etc. The whole point
of the sequential streams being the upper parts of the In/Out hierarchies is
so that non-indexible and non-rewindable streams would be catered for.
Indeed, it is more than likely that these would be the most often used
interfaces, even on entities that could support the derived interfaces.

I may not understand D well enough, but as I understand it, interfaces
cannot have ctors, so I don't think your this(InStream) thing will work.
Plus, by requiring the client code to explicitly create the cloned stream
instance, we would unnecessarily constrain the flexibility and/or efficiency
of the implementaion (e.g. clones of a certain implementing may be cached).

I also don't see why a Clone method could be confusing. It is a well-used,
and very useful, idiom: it allows one to "remember" a position and then pass
the interface off to another section of code to use, and vice versa.



"Jon Allen" <jallen minotstateu.edu> wrote in message
news:b5ivod$8ok$1 digitaldaemon.com...
 I like it, but I too have a couple suggestions.  In C++ I would use char*
 (for binary or text, or anything else). However, Walter was kind enough to
 provide us with a byte[] type which I'm assuming is for those kinds of
 ambiguities.

 I also think that maybe a buffered interface isn't completely necessary.

 As far as seeking and a start index, well they are a great idea for

 like files and the like, but what about streeaming data from sockets,
 keyboards, or anything else that is a little more dynamic?  I suggest
 leaving off the start index and just using a length and minlength.  Seeks
 could be implemented in classes where they make sense, instead of being

 of the interface.  Seek functions would of course be used to replace the
 start index parameter.  You might lose a little speed that way, but I

 it'd be worth it.

 I also think a this(InStream) constructor would be a little more

 than a clone function, but I think either one could be annoying in an
 interface.

 "Matthew Wilson" <dmd synesis.com.au> wrote in message
 news:b5igjc$301c$1 digitaldaemon.com...
 It kind of seems nice, but there's always something extra that someone

 essential, that "your" interface has omitted and "theirs" has got. And

 versa.

 For instance, your InStream deals in char[]. What if the data is binary?
 Also, expressing the buffering in an interface seems very strange. One

 usually imagine that buffering be a characteristic of one concrete
 interface-implementer vs another.

 I actually think that a rationalised and D-ised version of the COM


 /
 sequential-stream interfaces would be the way to go. The interface deals

 binary stuff only, and different implementing classes can overlay
 characteristics such as bin-text conversion (cr-lf issues). Perhaps -


 this is just off the top of the head - it could look like this

 interface SequentialInStream
 {
   Read . . . // precise signature to be decided, but this would read

 of bytes
 }

 interface SequentialOutStream
 {
   Write . . . // precise signature to be decided, but this would write
 arrays of bytes
 }

 interface InStream
 {
   Seek . . . // tbd
   Clone . . . // tbd
   other methods tbd
 }

 interface OutStream
 {
   Seek . . . // tbd
   Clone . . . // tbd
   other methods tbd
 }

 Everything that operates on types could be layered on top of this, in
 implementing classes.

 I have a moderately elegant C++ template version of object instance
 streaming that is syntactically similar to MFC's UpdateData data


 thingy, in the sense that the in and out code is written once. The
 difference is that everything is resolved at compile time, and it's
 exceedingly quick. It layers stuff over a binary stream interface, and

 maybe be converted into D. I'm not yet sufficiently up to speed on D's
 templates to say yeah or nay.

 The point, however, is that I think the concrete aspects of streaming

 be as generic as poss - binary interface - to reduce feature creep which
 limits the number of possible interface-implementing types, and the
 object-streaming should be thin as possible - template stuff - to reduce
 code size and keep efficiency high. This basically answers the two main
 problems with C++'s iostreams.

 That all sounds super cool, but of course the real world impacts in


 if
 anything, comes between these two layers. This includes character string
 streaming, exception-handling, dealing with cr-lf, etc. etc. If this
 middle-layer can be sorted such that it is straightforward (in terms of
 normal operation and error handling), efficient, extensible by the
 programmer without needing much extension in the library, then I think

 have something rather fantastic.

 Even though I am first and foremost a C++ fan, I've hated the iostreams

 a _long_ time, and it would tickle me no end if we could come up with a
 Dstreams architecture that answered all the iostreams faults with

 Trumpeting that on the Boost newsgroup would give D a massive fillip. ;)

 Matthew

 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3E7C970C.52EE26CD deming-os.org...
 This, IMHO, would be a good family of standard interfaces for input
 streams.  Thoughts?

 BTW, I intentionally designed InStream such that it would be very easy
 to overload the array slicing operator....



 interface InStream
 {
 public:
     class StreamDataExpired_Error : public Error {};

 public:
     char[] Get(ulong startIndex,ulong endIndexPlusOne);
             // note that the indices here follow the [start..end+1]
 convention of array slicing
             // Get() may throw StreamDataExpired_Error if you attempt



 re-read an index
             //        you've read before.  Some classes, including the
 BufferingInStream interface,
             //        have the ability to buffer old data and so might
 not do that.
             // Get() will block until all the data up to endIndex is
 ready.
             // Get() will return a shorter array if you hit EOF.



 retval.length to see if this
             //         happened.
     char[] Get(ulong startIndex,ulong endIndexPlusOne,ulong
 minEndIndexPlusOne);
             // this is like Get(), but will only block to satisfy the
 minimum.  It will never return
             //        more than endIndex.  Like Get(), it may throw
 StreamDataExpired_Error.
 };

 interface BufferedInStream : InStream
 {
     // this interface is like InStream, but it buffers all data,
 allowing you to view it multiple times.
     // This interface is most useful when the stream you're modeling



 a mmap'd file or something
     // like that that will be static anyway.

 public:
     void ExpireDataUpTo(ulong endIndexPlusOne);
         // this tells the class that it can discard old buffered data



 to this index.  It may ignore you,
         // so there is no guarantee that a later call to
 Get(<expiredData>) will throw an error (unless
         // the class makes that guarantee)
 };

 --
 The Villagers are Online! http://villagersonline.com

 .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
 .[ (a version.of(English).(precise.more)) is(possible) ]
 ?[ you want.to(help(develop(it))) ]



Mar 22 2003
parent "Jon Allen" <jallen minotstateu.edu> writes:
 - I did mention bytes in the interface method descriptions, and that would
 indeed be the recommended interchange type (at least in the interfaces. As

 said, the implementing classes would provide suitable overloads)

So you did, my bad.
 - I forgot to show the inheritance relationship of InStream <-
 SequentialInStream, and OutString <- SequentialOutStream

Yeah, but it was kind of obvious, I probably should have noticed it. That's what I get for "skimming" pseudo-code :-)
 I don't get your point about seeking, indexes, sockets, etc.

My point is basically that I don't like having a start index as part of a top level interface like Burton had it, but rather: interface SequentialInStream{byte[] read(int length);} interface InStream:SequentialInStream{void seek(int pos);} I think that is what you are saying anyhow. Right?
 I may not understand D well enough, but as I understand it, interfaces
 cannot have ctors, so I don't think your this(InStream) thing will work.

You are right, I tried it and it doesn't work. This is annoying to me, anyone else?
 I also don't see why a Clone method could be confusing.

I don't think it would be confusing. My point is that the clone function is basically just constructing a new instance of the class anyhow, so it's just a special case to remember.
Plus, by requiring the client code to explicitly create the cloned stream
instance, we would unnecessarily constrain the flexibility and/or

of the implementaion

What can you do with this: blah blah=oldblab.clone(); that you can't do with this: blah blab=new blah(oldblab); just as easily and efficiently? Either way the client still has to explicitly create the instance. Of course it my arguments against a clone function are easily overcome by the fact that my alternative doesn't actually work. grrr :-)
Mar 22 2003