How do C++ streams behave polymorphically?

How is it that I can pass any stringstream, fstream, cout/cin, etc. as a std::iostream/std::istream/std::ostream and it will work as if it is polymorphic? A common example is the operator>> and operator<< functions for streams. How is this polymorphic behavior available without hard coding or virtual functions?
It does use virtual functions. iostream is a mess, but that's basically what it boils down to.

From what I recall, each istream/ostream object has a streambuf object (pointer?) that is uses for the actual i/o. streambuf is loaded with virtual functions which lets it behave polymorphically.
Ah, so they cheated. Thanks, that makes sense now. I wonder how hard it would be to implement a self-made stream class...

EDIT: Looking at it, it doesn't seem like I'd be able to make input and output streams for e.g. servers and clients over network connections. Well, it would have been cool.

Now I have to ask why it was designed this way...
Last edited on
I have tried repeatedly to create my own stream classes derived from iostream. I have never been successful.

IMO, It's is one of the worst designed libraries I've ever used. If you can figure it out, I would love an explanation.
I see it as a rather well-designed library, although boost.iostreams certainly makes it easier to write simple extensions.

As for streams over network connections, there are tcp::iostream and ssl::stream in boost.asio if you need them, or you could just wrap a socket in a stream with boost.iostreams.
although boost.iostreams certainly makes it easier to write simple extensions.


I don't know. IMO I shouldn't need a wrapper lib to extend a lib.

The fact that the iostream class cannot be easily derived from is a tremendous design flaw. What's the point of providing an abstract interface if the user isn't able to [reasonably] provide their own implementation of it?

And that's not even touching the half-assed state machine and wonky, misused << and >> operators.

I really hate iostream, actually. It's a steaming pile. Which is too bad because I like most of the rest of the standard lib.
Last edited on
MiiNiPaa:

That graph is overly simplistic and does not even mention streambuf, which does most of the actual work (and is critical to understanding if you want to provide your own stream class).
I don't think all of the answerers don't know the stream hierarchy.
> What's the point of providing an abstract interface if the user isn't able to
> [reasonably] provide their own implementation of it?

The stream classes are facades, not abstract interfaces. Repeat, the stream classes are facades, not abstract interfaces. And any competent programmer would be able to provide a custom facade of their own.



> IMO, It's is one of the worst designed libraries I've ever used.

You are entitled to your opinion, of course. Which you had stated several months back.


> I have tried repeatedly to create my own stream classes derived from iostream.
> I have never been successful.
> If you can figure it out, I would love an explanation.

You had stated this too, several months back.

I had then provided a working example of how to write a custom stream (an output tee-stream, IIRC), and Cubbi had given another working example (customization of the codecvt facet, IIRC).

If you keep trying to create custom stream classes, without first having a basic understanding of the architecture, the roles that the stream classes play, and the roles that the associated streambuf and the facet classes play, you are going to remain frustrated for ever.

For ready reference, I've dug out the link to that earlier thread:
http://www.cplusplus.com/forum/general/70019/
Last edited on
I don't see a problem with deriving from iostream, except that it's almost never necessary (and when it is necessary, it's usually just to provide convenience constructors)

Do you have a use case?
The stream classes are facades, not abstract interfaces. Repeat, the stream classes are facades, not abstract interfaces. And any competent programmer would be able to provide a custom facade of their own.


The facades themselves form the interface, whether or not it's truly abstract:

1
2
3
4
void function(ostream& s)
{
  s << "something";
}


Here, you're not necessarily expected to pass an actual ostream, but rather are expected to pass one of its derived types (ofstream, ostringstream, etc). Whether or not ostream is strictly abstract isn't necessarily my primary point.

I had then provided a working example of how to write a custom stream (an output tee-stream, IIRC), and Cubbi had given another working example (customization of the codecvt facet, IIRC).


I vaguely remember trying Cubbi's and it didn't work, but I might be remembering incorrectly (this was a long time ago). I also distinctly remember trying out a lib I got on line that was a wrapper for Physics_FS that provided an iostream interface... but that also didn't work.

Whether or not it was a problem with the libs or not, I didn't really care, as I didn't put that much (read: any) effort into exploring it further.

If you keep trying to create custom stream classes, without first having a basic understanding of the architecture, the roles that the stream classes play, and the roles that the associated streambuf and the facet classes play,


You are kind of making my point.

The concept here is very simple. ostream for example: There's a stream of data that goes somewhere. There's also some seek/tell functionality thrown in for good measure.

Whatever other complications are involved are unimportant. That's the core functionality, and that is what would need to be overridden in a custom implementation. We're talking 3 or 4 very simple functions. At most.

You're going on about the differences between the stream classes, facet classes, streambufs, and all this other intertwined BS. It's needlessly complicated. Those are implementation specific details that the user (me) should not have to care about.

I mean seriously. Just give me a "write" function to overload. You're going to have to write this data somewhere eventually -- let me hook into wherever in the complex chain that is done, and do it my own way. It really could be that simple (and in fact it is that simple in every other lib I've used that abstracts this process).

That it isn't done that way.... that not only are you exposed to all this complex detail but are also required to study and understand it is an extremely poor design. It makes the classes unreasonable to use.

you are going to remain frustrated for ever.


I'm not frustrated. I just don't use it anymore. I use one of the dozen available alternatives, written by all the other people who share my sentiment.

You don't really have to look far to find people who have complaints about iostream.



EDIT:

Reviewing that thread you linked, I did not try Cubbi's or your example. Reviewing them again now.

Though in that thread I seem to be making all the same points I'm making now. Heh.

Yeah I started looking at your tee classes here:
http://www.cplusplus.com/forum/general/64174/#msg347154

And I started reading up on streambuf again to see how they work, and I just have a really hard time caring. Maybe some day I'll revisit iostream but I doubt it will be any time soon. There are enough things about it besides this particular point that I don't like which make me not want to really bother.

@ Cubbi:

No, a simple convenience constructor is fine. I was mostly referring to creating your own streambuf implementation.
Last edited on
The concept here is very simple. ostream for example: There's a stream of data that goes somewhere

Isn't it a sign of success of a library, that its users are convinced it is simple?

They took the massively complex C library I/O with all of its streams, buffers, locales and facets, flags and states, classifications and conversions, made it object-oriented, user-extensible at every turn, and (if you ask me) a whole lot more sensible, thanks to the separation of concern and sound class hierarchies. Yet they kept the end user interface so simple that every beginner can still write cout << "hello world" or make their own classes serializable with just two operator overloads.

Just give me a "write" function to overload

boost.iostreams does exactly that: http://www.boost.org/doc/libs/release/libs/iostreams/doc/concepts/sink.html

That's the core functionality, and that is what would need to be overridden in a custom implementation. We're talking 3 or 4 very simple functions. At most.

Who's talking about more? overflow(), underflow(), and sync()
I keep looking at the virtual functions on this page:
http://www.cplusplus.com/reference/streambuf/streambuf/
I'm having a hard time understanding how I can skip all the buffer nonsense and forward input operations to something else and output operations to something else. I also haven't been able to compile Cubbi's example because I don't have access to my clang installation.
This is a good beginner's tutorial:
http://www.mr-edd.co.uk/blog/beginners_guide_streambuf
Topic archived. No new replies allowed.