digitalmars.D - My stream concept
- Regan Heath (37/37) Aug 02 2004 Typical stream implementations I have seen use the following heirarchy:
- Matthew (6/40) Aug 02 2004 They are remarkably similar to two interfaces - IInputStream & IOutputSt...
- parabolis (37/84) Aug 02 2004 I am not entirely clear on how what you are doing is different
- Regan Heath (26/106) Aug 02 2004 I have written the code to read different types etc once in my Stream
- parabolis (2/7) Aug 02 2004 What will your eq. version of the above be?
- Regan Heath (6/13) Aug 02 2004 I'm not at all sure yet.
- Ben Hinkle (30/78) Aug 02 2004 cool idea(s). I like the "bolt-in" (or it bolt-on?) stuff. One possible
- Regan Heath (23/105) Aug 02 2004 You're right. :(
- Vathix (1/6) Aug 02 2004 Why not use void[] for general read and write?
- Regan Heath (9/20) Aug 02 2004 So you can go:
- Sean Kelly (4/8) Aug 02 2004 This is kind of how the stream implementation I was working on is done. ...
Typical stream implementations I have seen use the following heirarchy: class Stream { } class FileStream : Stream { } class SocketStream : Stream { } in other words you have to design/derive a class from Stream for each type of Stream you want to implement. I always thought this was kinda backwards, and limited, in fact after Arcane Jill said something along the lines of "anything that reads and writes bytes _is_ a stream" I got to thinking surely I can use templates to make that statement true. The way I have done it is to define some interfaces: interface InputStream { ulong read(void* buffer, ulong length); ulong available(); bool eof(); } interface OutputStream { ulong write(void* buffer, ulong length); } and a Stream and BufferedStream template class. Any object that supports those interfaces (as does my RawFile class) can then be extended by the two template classes to give full stream functionality by some simple aliases, example: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; I have attached the code, it is far from complete and probably buggy as heck as I have not done any testing (or unittests - I know I'm bad) this was simply proof of concept. Any and all comments are welcome/desired. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Aug 02 2004
"Regan Heath" <regan netwin.co.nz> wrote in message news:opsb4onie15a2sq9 digitalmars.com...Typical stream implementations I have seen use the following heirarchy: class Stream { } class FileStream : Stream { } class SocketStream : Stream { } in other words you have to design/derive a class from Stream for each type of Stream you want to implement. I always thought this was kinda backwards, and limited, in fact after Arcane Jill said something along the lines of "anything that reads and writes bytes _is_ a stream" I got to thinking surely I can use templates to make that statement true. The way I have done it is to define some interfaces: interface InputStream { ulong read(void* buffer, ulong length); ulong available(); bool eof(); } interface OutputStream { ulong write(void* buffer, ulong length); } and a Stream and BufferedStream template class. Any object that supports those interfaces (as does my RawFile class) can then be extended by the two template classes to give full stream functionality by some simple aliases, example: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; I have attached the code, it is far from complete and probably buggy as heck as I have not done any testing (or unittests - I know I'm bad) this was simply proof of concept. Any and all comments are welcome/desired.They are remarkably similar to two interfaces - IInputStream & IOutputStream - that I've recently written for a client's networking project. I can't include the code, of course. ;) I agree with the general principle you outline. IStream is one of the elegant parts of COM. (IStorage one of the hideous ones.<g>).
Aug 02 2004
Regan Heath wrote:Typical stream implementations I have seen use the following heirarchy: class Stream { } class FileStream : Stream { } class SocketStream : Stream { } in other words you have to design/derive a class from Stream for each type of Stream you want to implement. I always thought this was kinda backwards, and limited, in fact after Arcane Jill said something along the lines of "anything that reads and writes bytes _is_ a stream" I got to thinking surely I can use templates to make that statement true. The way I have done it is to define some interfaces: interface InputStream { ulong read(void* buffer, ulong length); ulong available(); bool eof(); } interface OutputStream { ulong write(void* buffer, ulong length); } and a Stream and BufferedStream template class. Any object that supports those interfaces (as does my RawFile class) can then be extended by the two template classes to give full stream functionality by some simple aliases, example: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; I have attached the code, it is far from complete and probably buggy as heck as I have not done any testing (or unittests - I know I'm bad) this was simply proof of concept. Any and all comments are welcome/desired. ReganI am not entirely clear on how what you are doing is different from the typical stream implementations you have seen. Look through the thread "OT scanf in Java" for a long post of mine towards the top that explains why the Stream implementations that you think of as being backwards actually do exactly what you want. To give a hint you should ask yourself how you can combine these streams: (--Stream means InputStream or OutputStream) File--Stream Network--Stream Memory--Stream Buffered--Stream Compress--Stream CRC32 Digest--Stream (eg CRC32 or MD5) Image--Stream Video--Stream Audio--Stream Sometimes I will want my data to come from a File, other times I want it to come from the Network. Sometimes I want a digest of the compressed data other times I want a digest of the uncompressed data. These actions are performed in the order I want based on the order in which I construct them: new ImageStream( DigestStream( CompressStream ( FileStream ) ) ) This would read file data, decompresses the data and then compute the CRC32 of the uncompressed data and read image data from it. However: new ImageStream( CompressStream ( DigestStream( FileStream ) ) ) This would read file data, compute the CRC32 of the uncompressed data and then decompress the data and read image data from it. I suspect the class you want to implement will either duplicate the way Stream libraries work or you will find yourself writing seperate classes to handle the two cases above which is bad. Just try to write out all the possible combinations that these 9 classes have already accomplished and you will see what I mean.
Aug 02 2004
On Mon, 02 Aug 2004 20:21:28 -0400, parabolis <parabolis softhome.net> wrote:Regan Heath wrote:I have written the code to read different types etc once in my Stream template, and can apply it to _any_ class at all that implements one read method of the form: ulong read(void* buffer, ulong length); the same applies to writing. So, if you added methods in the forms given in my interfaces to std.socket you could say: alias Stream!(Socket) SocketStream; alias BufferedStream!(SocketStream,2048) BufferedSocket; and you have a socket you can instantiate like so: SocketStream s = new SocketStream(); which is a buffered stream.Typical stream implementations I have seen use the following heirarchy: class Stream { } class FileStream : Stream { } class SocketStream : Stream { } in other words you have to design/derive a class from Stream for each type of Stream you want to implement. I always thought this was kinda backwards, and limited, in fact after Arcane Jill said something along the lines of "anything that reads and writes bytes _is_ a stream" I got to thinking surely I can use templates to make that statement true. The way I have done it is to define some interfaces: interface InputStream { ulong read(void* buffer, ulong length); ulong available(); bool eof(); } interface OutputStream { ulong write(void* buffer, ulong length); } and a Stream and BufferedStream template class. Any object that supports those interfaces (as does my RawFile class) can then be extended by the two template classes to give full stream functionality by some simple aliases, example: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; I have attached the code, it is far from complete and probably buggy as heck as I have not done any testing (or unittests - I know I'm bad) this was simply proof of concept. Any and all comments are welcome/desired. ReganI am not entirely clear on how what you are doing is different from the typical stream implementations you have seen.Look through the thread "OT scanf in Java" for a long post of mine towards the top that explains why the Stream implementations that you think of as being backwards actually do exactly what you want. To give a hint you should ask yourself how you can combine these streams:Combination is the next step, what I have so far is not intended to handle combinations.(--Stream means InputStream or OutputStream) File--Stream Network--Stream Memory--Stream Buffered--Stream Compress--Stream CRC32 Digest--Stream (eg CRC32 or MD5) Image--Stream Video--Stream Audio--Stream Sometimes I will want my data to come from a File, other times I want it to come from the Network. Sometimes I want a digest of the compressed data other times I want a digest of the uncompressed data. These actions are performed in the order I want based on the order in which I construct them: new ImageStream( DigestStream( CompressStream ( FileStream ) ) ) This would read file data, decompresses the data and then compute the CRC32 of the uncompressed data and read image data from it. However: new ImageStream( CompressStream ( DigestStream( FileStream ) ) ) This would read file data, compute the CRC32 of the uncompressed data and then decompress the data and read image data from it.My next step is to implement what I call 'filters' they are like your CompressStream etc above, they will link one stream to another and filter the data passing through them.I suspect the class you want to implement will either duplicate the way Stream libraries work or you will find yourself writing seperate classes to handle the two cases above which is bad. Just try to write out all the possible combinations that these 9 classes have already accomplished and you will see what I mean.As I said above, combinations are my next step. I may end up handling them in the same way as you have shown above but I'd rather not, I dislike instantiation in the form: new ImageStream( CompressStream ( DigestStream( FileStream ) ) ) Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Aug 02 2004
Regan Heath wrote:As I said above, combinations are my next step. I may end up handling them in the same way as you have shown above but I'd rather not, I dislike instantiation in the form: new ImageStream( CompressStream ( DigestStream( FileStream ) ) )What will your eq. version of the above be?
Aug 02 2004
On Mon, 02 Aug 2004 21:00:57 -0400, parabolis <parabolis softhome.net> wrote:Regan Heath wrote:I'm not at all sure yet. Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/As I said above, combinations are my next step. I may end up handling them in the same way as you have shown above but I'd rather not, I dislike instantiation in the form: new ImageStream( CompressStream ( DigestStream( FileStream ) ) )What will your eq. version of the above be?
Aug 02 2004
Regan Heath wrote:Typical stream implementations I have seen use the following heirarchy: class Stream { } class FileStream : Stream { } class SocketStream : Stream { } in other words you have to design/derive a class from Stream for each type of Stream you want to implement. I always thought this was kinda backwards, and limited, in fact after Arcane Jill said something along the lines of "anything that reads and writes bytes _is_ a stream" I got to thinking surely I can use templates to make that statement true. The way I have done it is to define some interfaces: interface InputStream { ulong read(void* buffer, ulong length); ulong available(); bool eof(); } interface OutputStream { ulong write(void* buffer, ulong length); } and a Stream and BufferedStream template class. Any object that supports those interfaces (as does my RawFile class) can then be extended by the two template classes to give full stream functionality by some simple aliases, example: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; I have attached the code, it is far from complete and probably buggy as heck as I have not done any testing (or unittests - I know I'm bad) this was simply proof of concept. Any and all comments are welcome/desired. Regancool idea(s). I like the "bolt-in" (or it bolt-on?) stuff. One possible problem, though, is that if read(out int x), for example, is in Stream!(File) and it call super.read(...) then the BufferedStream!(FileStream) subclass will not read ints from the buffer (the super.read goes directly to RawFile.read - not BufferedStream.read). I guess I'm a little lost about how the buffering works. At one point there was the suggestion of moving the default implementations of all the read(out int x) etc from Stream (the original one in std/stream.d) to a mixin so that instead of subclassing to get the default implementation you mix it in. I don't know how that approach would change around your code but it's worth thinking about. To be more concrete, here's how that would work: template DefaultInputStream() { void read(out int x) {readExact(...);} char[] readLine() {...} ... } template DefaultOutputStream() { void write(int x) {...} void writeLine(char[] x) {...} ... } class Stream : InputStream, OutputStream { mixin DefaultInputStream; mixin DefaultOutputStream; ... } Anyhow, keep pluggin'. -Ben
Aug 02 2004
On Mon, 02 Aug 2004 22:19:19 -0400, Ben Hinkle <bhinkle4 juno.com> wrote:Regan Heath wrote:Yeah, I think they're a great idea.Typical stream implementations I have seen use the following heirarchy: class Stream { } class FileStream : Stream { } class SocketStream : Stream { } in other words you have to design/derive a class from Stream for each type of Stream you want to implement. I always thought this was kinda backwards, and limited, in fact after Arcane Jill said something along the lines of "anything that reads and writes bytes _is_ a stream" I got to thinking surely I can use templates to make that statement true. The way I have done it is to define some interfaces: interface InputStream { ulong read(void* buffer, ulong length); ulong available(); bool eof(); } interface OutputStream { ulong write(void* buffer, ulong length); } and a Stream and BufferedStream template class. Any object that supports those interfaces (as does my RawFile class) can then be extended by the two template classes to give full stream functionality by some simple aliases, example: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; I have attached the code, it is far from complete and probably buggy as heck as I have not done any testing (or unittests - I know I'm bad) this was simply proof of concept. Any and all comments are welcome/desired. Regancool idea(s). I like the "bolt-in" (or it bolt-on?) stuff.One possible problem, though, is that if read(out int x), for example, is in Stream!(File) and it call super.read(...) then the BufferedStream!(FileStream) subclass will not read ints from the buffer (the super.read goes directly to RawFile.read - not BufferedStream.read). I guess I'm a little lost about how the buffering works.You're right. :( I think to fix it I change: alias Stream!(RawFile) FileStream; alias BufferedStream!(FileStream,2048) File; to alias BufferedStream!(RawFile,2048) File; alias Stream!(File) FileStream; and then change the signatures of the methods in BufferedStream from ulong read(ubyte[] data) to ulong read(void* buffer, ulong length) etc. This highlights an 'inclusion order' weakness to this design which needs to be fixed.At one point there was the suggestion of moving the default implementations of all the read(out int x) etc from Stream (the original one in std/stream.d) to a mixin so that instead of subclassing to get the default implementation you mix it in. I don't know how that approach would change around your code but it's worth thinking about.I think this might just be the solution to the inclusion order weakness in my current design. I'm going to turn my Stream template class into a pair of mixins.To be more concrete, here's how that would work: template DefaultInputStream() { void read(out int x) {readExact(...);} char[] readLine() {...} ... } template DefaultOutputStream() { void write(int x) {...} void writeLine(char[] x) {...} ... } class Stream : InputStream, OutputStream { mixin DefaultInputStream; mixin DefaultOutputStream; ... } Anyhow, keep pluggin'.Thanks. I'll post version 2 when I have one. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Aug 02 2004
and then change the signatures of the methods in BufferedStream from ulong read(ubyte[] data) to ulong read(void* buffer, ulong length) etc.Why not use void[] for general read and write?
Aug 02 2004
On Tue, 3 Aug 2004 00:19:15 -0400, Vathix <vathixSpamFix dprogramming.com> wrote:So you can go: bool read(out byte x) { if (this.read(&x,x.sizeof) ..etc.. bool read(out ubyte x) { if (this.read(&x,x.sizeof) ..etc.. bool read(out short x) { if (this.read(&x,x.sizeof) ..etc.. Regan. -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/and then change the signatures of the methods in BufferedStream from ulong read(ubyte[] data) to ulong read(void* buffer, ulong length) etc.Why not use void[] for general read and write?
Aug 02 2004
In article <cemsn6$kb9$1 digitaldaemon.com>, Ben Hinkle says...At one point there was the suggestion of moving the default implementations of all the read(out int x) etc from Stream (the original one in std/stream.d) to a mixin so that instead of subclassing to get the default implementation you mix it in.This is kind of how the stream implementation I was working on is done. If I find the time to finish it I'll post it. Sean
Aug 02 2004