digitalmars.D - Stream, Socket == SocketStream...
- Regan Heath (69/69) Jun 08 2005 Ok,
- Ben Hinkle (25/90) Jun 09 2005 I tried factoring out TInputStream and TOutputStream a while ago but the...
- Kris (8/27) Jun 09 2005 buffering
- Ben Hinkle (11/41) Jun 09 2005 If the goal is to make an interface that can be the backing stream for a...
- Regan Heath (21/70) Jun 09 2005 I dont think that is the 'goal'. The goal is to have a logical set of
- Ben Hinkle (16/54) Jun 09 2005 BufferedStream works fine with non-seekable streams. Kris's design is to...
- Regan Heath (37/67) Jun 09 2005 Yep.
- Kris (7/12) Jun 09 2005 allows
- Carlos Santander (4/10) Jun 09 2005 How about std.socketstream?
- Regan Heath (6/13) Jun 09 2005 It's almost exactly the same as the code I provided and has the same
- Regan Heath (21/21) Jun 20 2005 Being dissatisfied with the current stream implementation (no offence
Ok, I want to create a class/wrapper that lets me call Stream methods i.e. readLine, writeLine, etc. But, also lets me call Socket methods i.e. 'connect' eg. version(build) {pragma(link, wsock32);} import std.socket; import std.stream; import std.stdio; class SocketStream : Stream { this(Socket s) { _socket = s; readable = true; writeable = true; seekable = false; isopen = _socket.isAlive(); } override size_t readBlock(void* result, size_t len) { void[] tmp = result[0..len]; return socket.receive(tmp); } override size_t writeBlock(void* result, size_t len) { void[] tmp = result[0..len]; return socket.send(tmp); } override ulong seek(long offset, SeekPos whence) { assert(0); } private: Socket _socket; } int main(char[][] args) { BufferedStream s = new BufferedStream(new SocketStream(new TcpSocket())); //this is not possible s.connect(new InternetAddress("www.digitalmars.com",80)); //these work. s.writeLine("GET / HTTP/1.0"); s.writeLine(""); writefln("%s",s.readLine()); } Before anyone points it out, yes, I realise I can put the hostname and port in the constructor for TcpSocket. I don't want to do that. Before anyone (Kris) directs me to another library which contains an existing SocketStream like class, thanks, but I'm also interested in how you solve the general problem. It seems to me, that multiple inheritance would let me do this, so, what's the D solution? My first thought is that if we took the Stream class methods, placed them in templates TInputStream and TOutputStream, then I could mix them into my class. Meaning, I could derive my SocketStream above from "TcpSocket" and mixin the TInputStream and TOutputStream methods. But! doesn't that mean it's not technically a "Stream" and as such so I wouldn't be able to use it with BufferedStream? If so, ideally I'd want a "Stream" interface to derive from also. That would mean the Stream class would have to go in favour of 3 interfaces (InputStream, OutputStream, Stream) and 2 templates (TInputStream, TOutputStream). Thoughts? Ideas? Regan
Jun 08 2005
"Regan Heath" <regan netwin.co.nz> wrote in message news:opsr2zs8mt23k2f5 nrage.netwin.co.nz...Ok, I want to create a class/wrapper that lets me call Stream methods i.e. readLine, writeLine, etc. But, also lets me call Socket methods i.e. 'connect' eg. version(build) {pragma(link, wsock32);} import std.socket; import std.stream; import std.stdio; class SocketStream : Stream { this(Socket s) { _socket = s; readable = true; writeable = true; seekable = false; isopen = _socket.isAlive(); } override size_t readBlock(void* result, size_t len) { void[] tmp = result[0..len]; return socket.receive(tmp); } override size_t writeBlock(void* result, size_t len) { void[] tmp = result[0..len]; return socket.send(tmp); } override ulong seek(long offset, SeekPos whence) { assert(0); } private: Socket _socket; } int main(char[][] args) { BufferedStream s = new BufferedStream(new SocketStream(new TcpSocket())); //this is not possible s.connect(new InternetAddress("www.digitalmars.com",80)); //these work. s.writeLine("GET / HTTP/1.0"); s.writeLine(""); writefln("%s",s.readLine()); } Before anyone points it out, yes, I realise I can put the hostname and port in the constructor for TcpSocket. I don't want to do that. Before anyone (Kris) directs me to another library which contains an existing SocketStream like class, thanks, but I'm also interested in how you solve the general problem. It seems to me, that multiple inheritance would let me do this, so, what's the D solution? My first thought is that if we took the Stream class methods, placed them in templates TInputStream and TOutputStream, then I could mix them into my class.I tried factoring out TInputStream and TOutputStream a while ago but the private unget buffer couldn't go into the mixin so I didn't pursue it very far (you can't mix in private members). We could just make the unget buffer public and live with it.Meaning, I could derive my SocketStream above from "TcpSocket" and mixin the TInputStream and TOutputStream methods. But! doesn't that mean it's not technically a "Stream" and as such so I wouldn't be able to use it with BufferedStream? If so, ideally I'd want a "Stream" interface to derive from also. That would mean the Stream class would have to go in favour of 3 interfaces (InputStream, OutputStream, Stream) and 2 templates (TInputStream, TOutputStream). Thoughts? Ideas? ReganStream should be pretty much the same as the union of InputStream and OutputStream with the missing functions being the seeking functions and read/writeBlock. You've given a good argument for making BufferedStream wrap an abbreviated interface that just supplies the readBlock, writeBlock and seek members (and maybe one or two other things I've forgotten the buffering needs). Maybe call this interface ... something like BasicStream or SimpleStream. It would look something like interface BasicStream { size_t readBlock(void* buffer, size_t size); size_t writeBlock(void* buffer, size_t size); ulong seek(long offset, SeekPos rel); } class Stream : InputStream, OutputStream, BasicStream { ... } class BufferedStream : Stream { BasicStream s; this(BasicStream s) {this.s = s; ... } } That way your class wouldn't subclass Stream but would implement some or all of the interfaces.
Jun 09 2005
"Ben Hinkle" <ben.hinkle gmail.com> wrote ...Stream should be pretty much the same as the union of InputStream and OutputStream with the missing functions being the seeking functions and read/writeBlock. You've given a good argument for making BufferedStreamwrapan abbreviated interface that just supplies the readBlock, writeBlock and seek members (and maybe one or two other things I've forgotten thebufferingneeds). Maybe call this interface ... something like BasicStream or SimpleStream. It would look something like interface BasicStream { size_t readBlock(void* buffer, size_t size); size_t writeBlock(void* buffer, size_t size); ulong seek(long offset, SeekPos rel); } class Stream : InputStream, OutputStream, BasicStream { ... } class BufferedStream : Stream { BasicStream s; this(BasicStream s) {this.s = s; ... } } That way your class wouldn't subclass Stream but would implement some orallof the interfaces.BasicStream should probably not have a seek() method, since not all streams are seekable (socket, for example). This kind of issue, and the limitations implied via Decorators, is why mango.io took the route it did. It doesn't exhibit these problems, is more flexible, and is simpler to use ;)
Jun 09 2005
"Kris" <fu bar.com> wrote in message news:d8a2n1$22pn$1 digitaldaemon.com..."Ben Hinkle" <ben.hinkle gmail.com> wrote ...If the goal is to make an interface that can be the backing stream for a BufferedStream then seek() is needed. Streams throw if they aren't seekable - as they throw if they aren't readable or writeable (or writable...) Anyway, some interface for the backing stream for BufferedStream, EndianStream and SliceStream will probably be fine given the template mixins. The name BasicStream doesn't sound quite right so any suggestions are welcome. Glancing at those wrapper streams the interface will probably have to inherit both InputStream and OutputStream and add one or two seek members.Stream should be pretty much the same as the union of InputStream and OutputStream with the missing functions being the seeking functions and read/writeBlock. You've given a good argument for making BufferedStreamwrapan abbreviated interface that just supplies the readBlock, writeBlock and seek members (and maybe one or two other things I've forgotten thebufferingneeds). Maybe call this interface ... something like BasicStream or SimpleStream. It would look something like interface BasicStream { size_t readBlock(void* buffer, size_t size); size_t writeBlock(void* buffer, size_t size); ulong seek(long offset, SeekPos rel); } class Stream : InputStream, OutputStream, BasicStream { ... } class BufferedStream : Stream { BasicStream s; this(BasicStream s) {this.s = s; ... } } That way your class wouldn't subclass Stream but would implement some orallof the interfaces.BasicStream should probably not have a seek() method, since not all streams are seekable (socket, for example). This kind of issue, and the limitations implied via Decorators, is why mango.io took the route it did. It doesn't exhibit these problems, is more flexible, and is simpler to use ;)
Jun 09 2005
On Thu, 9 Jun 2005 17:15:02 -0400, Ben Hinkle <bhinkle mathworks.com> wrote:"Kris" <fu bar.com> wrote in message news:d8a2n1$22pn$1 digitaldaemon.com...I dont think that is the 'goal'. The goal is to have a logical set of classes/templates/interfaces that define how streams are to be handled in D. Some streams aren't seekable. BufferedStream can only operate on a stream which is seekable? that doesn't seem right. What if I want to buffer my input, but never plan to seek it? It seems to me we have 3 properties Readability, Writability and Seekability, a stream may have 1, 2 or all 3 of them. So we need 3 seperate interfaces, 3 seperate templates to mixin, and some way of saying "I require a stream which can read and seek" or "write and seek" or "read and write" or "read and write and seek"."Ben Hinkle" <ben.hinkle gmail.com> wrote ...If the goal is to make an interface that can be the backing stream for a BufferedStream then seek() is needed.Stream should be pretty much the same as the union of InputStream and OutputStream with the missing functions being the seeking functions and read/writeBlock. You've given a good argument for making BufferedStreamwrapan abbreviated interface that just supplies the readBlock, writeBlock and seek members (and maybe one or two other things I've forgotten thebufferingneeds). Maybe call this interface ... something like BasicStream or SimpleStream. It would look something like interface BasicStream { size_t readBlock(void* buffer, size_t size); size_t writeBlock(void* buffer, size_t size); ulong seek(long offset, SeekPos rel); } class Stream : InputStream, OutputStream, BasicStream { ... } class BufferedStream : Stream { BasicStream s; this(BasicStream s) {this.s = s; ... } } That way your class wouldn't subclass Stream but would implement some orallof the interfaces.BasicStream should probably not have a seek() method, since not all streams are seekable (socket, for example). This kind of issue, and the limitations implied via Decorators, is why mango.io took the route it did. It doesn't exhibit these problems, is more flexible, and is simpler to use ;)Streams throw if they aren't seekable - as they throw if they aren't readable or writeable (or writable...)Wouldn't it be better to know at compile time that it's not seekable. As in: void foo(ISeekable a) {} SocketStream s = new SocketStream(); foo(s); //compile time error. We can _also_ throw if the seekable nature of a stream changes.Anyway, some interface for the backing stream for BufferedStream, EndianStream and SliceStream will probably be fine given the template mixins. The name BasicStream doesn't sound quite right so any suggestions are welcome. Glancing at those wrapper streams the interface will probably have to inherit both InputStream and OutputStream and add one or two seek members.It's a stream which can read, write, and seek. SeekStream might not be too bad a name. Regan
Jun 09 2005
BufferedStream works fine with non-seekable streams. Kris's design is to make seekable a compile-time decision. std.stream makes seekable a run-time decision. It's handy to make seekable a run-time decision since that allows better integration with std.c.stdio and the OS concept of files since pipes are considered files.I dont think that is the 'goal'. The goal is to have a logical set of classes/templates/interfaces that define how streams are to be handled in D. Some streams aren't seekable. BufferedStream can only operate on a stream which is seekable? that doesn't seem right. What if I want to buffer my input, but never plan to seek it?BasicStream should probably not have a seek() method, since not all streams are seekable (socket, for example). This kind of issue, and the limitations implied via Decorators, is why mango.io took the route it did. It doesn't exhibit these problems, is more flexible, and is simpler to use ;)If the goal is to make an interface that can be the backing stream for a BufferedStream then seek() is needed.It seems to me we have 3 properties Readability, Writability and Seekability, a stream may have 1, 2 or all 3 of them. So we need 3 seperate interfaces, 3 seperate templates to mixin, and some way of saying "I require a stream which can read and seek" or "write and seek" or "read and write" or "read and write and seek".Yup that's a possiblity. The question is how many interfaces does one need? If we had Read/Write/Seek and the combinations of those then that's seven total interfaces. I propose three of those cover enough surface area: Read, Write and Read+Write+Seek. If one wants one of the other interface then the run-time decisions ala Stream are available. The Read interface is InputStream; Write is OutputStream and Read+Write+Seek is <name TDB>.The use cases of a function that takes ISeekable is very small (ie I can't think of any).Streams throw if they aren't seekable - as they throw if they aren't readable or writeable (or writable...)Wouldn't it be better to know at compile time that it's not seekable. As in: void foo(ISeekable a) {} SocketStream s = new SocketStream(); foo(s); //compile time error. We can _also_ throw if the seekable nature of a stream changes.To me SeekStream would just contain the seek methods and wouldn't cover the rest of streaming. So off the top of my head something more inclusive would be better.Anyway, some interface for the backing stream for BufferedStream, EndianStream and SliceStream will probably be fine given the template mixins. The name BasicStream doesn't sound quite right so any suggestions are welcome. Glancing at those wrapper streams the interface will probably have to inherit both InputStream and OutputStream and add one or two seek members.It's a stream which can read, write, and seek. SeekStream might not be too bad a name.
Jun 09 2005
On Thu, 9 Jun 2005 19:21:44 -0400, Ben Hinkle <ben.hinkle gmail.com> wrote:BufferedStream works fine with non-seekable streams.Ok.Kris's design is to make seekable a compile-time decision.Yep.std.stream makes seekable a run-time decision.A runtime decision *only*.It's handy to make seekable a run-time decision since that allows better integration with std.c.stdio and the OS concept of files since pipes are considered files.It's handier to do both (compile+runtime)True. But what's wrong with that? --basic InputStream OutputStream SeekStream --combinations InputSeekStream OutputSeekStream InputOutputStream --full InputOutputSeekStream (or simply "Stream" for brevity) It does get kinda long winded, but, you'd use the combinations rarely. Q: Why include the word 'Stream' at all? Q: What is a stream anyway? It seems to me a "Stream" is anything you can read bytes from *or* anything you can write bytes to, my definition has never included seekability. So, IMO, a "Stream" should not be seekable at all. Nor would it include any sort of unget or other advanced idea. Of course, we want seekable things, we want unget, we want buffering, we'd probably never directly use something that didn't have one of those, but, my feeling is that they should be factored in outside of the basic "Stream" interface, i.e. in BufferedStream perhaps. I think perhaps I should have a go at designing something myself, at least then I'll have something concrete to bring to the table. To be honest I am relatively new to the whole Stream concept, being from a C (not C++) background.It seems to me we have 3 properties Readability, Writability and Seekability, a stream may have 1, 2 or all 3 of them. So we need 3 seperate interfaces, 3 seperate templates to mixin, and some way of saying "I require a stream which can read and seek" or "write and seek" or "read and write" or "read and write and seek".Yup that's a possiblity. The question is how many interfaces does one need? If we had Read/Write/Seek and the combinations of those then that's seven total interfaces.True, I was assuming it would also read or write, the example would be better asking for the combination of seek + read or write. I guess the only point I'm making is that it would be nice to be able to select/check seekability at compile time as well as runtime (which we currently have). ReganThe use cases of a function that takes ISeekable is very small (ie I can't think of any).Streams throw if they aren't seekable - as they throw if they aren't readable or writeable (or writable...)Wouldn't it be better to know at compile time that it's not seekable. As in: void foo(ISeekable a) {} SocketStream s = new SocketStream(); foo(s); //compile time error. We can _also_ throw if the seekable nature of a stream changes.
Jun 09 2005
"Ben Hinkle" <ben.hinkle gmail.com> wrote...BufferedStream works fine with non-seekable streams. Kris's design is to make seekable a compile-time decision. std.stream makes seekable arun-timedecision. It's handy to make seekable a run-time decision since thatallowsbetter integration with std.c.stdio and the OS concept of files sincepipesare considered files.Correction: the developer has a choice with mango.io ~ use compile-time or runtime checking ~ one is not forced down a single path. This is not true of phobos.io, but it's really not that important <g>
Jun 09 2005
Regan Heath escribió:Ok, I want to create a class/wrapper that lets me call Stream methods i.e. readLine, writeLine, etc. But, also lets me call Socket methods i.e. 'connect' eg.How about std.socketstream? -- Carlos Santander Bernal
Jun 09 2005
On Thu, 09 Jun 2005 15:20:08 -0500, Carlos Santander <csantander619 gmail.com> wrote:Regan Heath escribió:It's almost exactly the same as the code I provided and has the same problems (which aren't really huge problems, it's just not quite what I'd prefer). ReganOk, I want to create a class/wrapper that lets me call Stream methods i.e. readLine, writeLine, etc. But, also lets me call Socket methods i.e. 'connect' eg.How about std.socketstream?
Jun 09 2005
Being dissatisfied with the current stream implementation (no offence intended Ben you do some great work) I've had a play with various stream ideas. I keep coming back to seperating input and output, it makes the most sense to me, but it's not my preffered choice for usability i.e. it's nice to have a single object/class and both read and write to/from it. I can't seem to achieve this, how I'd like, without some sort of multiple inheritance. What are peoples favourite stream libraries (I've poked my nose into the Java one and the mango one, what else should I look at for inspiration). I found a good solution to the whole seekability problem. You define a seekable interface, eg. interface ISeekable { size_t seek(size_t offset, SeekPos from); ..etc.. } then you can use "static if" to check for seekability (rather than having a boolean flag in the class. The same applies to readability and writability. This way you can have compile time checking (I believe). Regan
Jun 20 2005