Deriving from streambuf without rewriting a corresponding stream
Some days ago, I decided that it would be fun to write a streambuf
subclass that would use mmap
and read-ahead.
I looked at how my STL (SGI) implemented filebuf
and realized that basic_filebuf
contains a FILE*
. So inheriting from basic_filebuf
is out of the question.
So I inherited from basic_streambuf
. Then i wanted to bind my mmapbuf
to a fstream.
I thought the only thing that I would have to do would be to copy the implicit interface of filebuf
... but that was a clear mistake. In the SGI, basic_fstream
owns a basic_filebuf
. No matter if I call basic_filestream.std::::ios::rdbuf( streambuf* )
, the filestream completely ignores it and uses its own filebuf
.
So now I'm a bit confused... sure, I can create my own mmfstream
, that would be the exact copy/paste of the fstream
but that sounds really not DRY-oriented.
What I can't understand, is: why does fstream
is so tightly coupled with filebuf
, so that it is not possible to use anything else than a filebuf
? The whole point of separating streams and bufs is that one can use a stream with a different buffer.
Solutions:
=> file开发者_Go百科stream
should rely on the implicit interface of filebuf
. That is, fstream should be templated by a streambuf class. That would allow everyone to provide its own streambuf subclass to a fstream
as long as it implements filebuf
's implicit interface. Problem: we cannot add a template parameter to fstream
since it would break template selectors while using fstream
as template template parameter.
=> filebuf
should be a pure virtual class without any additional attributes. So that one can inherit from it without carrying all its FILE* garbage.
Your ideas on the subject ?
In the IO streams' design, most of the actual streams' functionality (as opposed to the stream buffers' functionality) is implemented in std::basic_istream
, std::basic_ostream
, and their base classes. The string and file stream classes are more or less just convenience wrappers which make sure a stream with the right type of buffer is instantiated.
If you want to extend the streams, you almost always want to provide your own stream buffer class, and you almost never need to provide your own stream class. .
Once you have your own stream buffer type, you can then make it the buffer for any stream object you happen to have around. Or you derive your own classes from std::basic_istream
, std::basic_ostream
, and std::basic_iostream
which instantiates your stream buffer and pass it to their base classes.
The latter is more convenient for users, but requires you to write some boiler-plate code for the buffer's instantiation (namely constructors for the stream class).
To answer your question: File streams and file buffer are coupled so tightly because the former only exists to ease the creation of the latter. Using a file stream makes it easy to set it all up.
Using your own stream class to wrap construction of your own stream buffer shouldn't be a problem, since you shouldn't be passing around file streams anyway, but only (references) to the base classes.
Check out mapped_file in the Boost.Iostreams library. I've never used used it myself, but it seems like it might already do what you need.
EDIT: Oops, reread your questions and I see you're doing this for fun. Perhaps you can draw inspiration from Boost.Iostreams?
fstream
in itself is not a big class. It inherits from basic_stream
to provide support for all the <<
and >>
operations, contains a specialized steambuf
that have to be initialized, and the corresponding constructors to pass the parameters to the streambuf
constructor.
In a sense, what you wrote about your templated solution is OK. But basic_stream
can also be derived into a tcp_stream
for example. In that case, the constructors of fstream
are a bit useless. Thus you need to provide a new tcpstream
class, inheriting from basic_stream
with the correct parameters for the constructors to be able to create the tcp_stream
. In the end, you wouldn't use anything from fstream
. Creating this new tcpstream
is a matter of writing 3 or 4 functions only.
In the end, you would derive from the fstream
class without any real reason to. This would add more coupling in the class hierarchy, unneeded coupling.
The whole point of std::fstream
is that it is a _F_ile based std::stream
. If you want an ordinary std::stream
backed by your mmstreambuf
, then you should create a mmstreambuf
and pass it to std::stream::stream(std::streambuf*)
精彩评论