jak@cs.brown.edu (Jak Kirman) (01/31/91)
I would like to create a type which behaves precisely as does an ostream
except that the constructor would take two ostream references, and every
operation would affect both ostreams equally.
This would be very useful for debugging, where I often want a complete
transcript of the debugging messages in a file, and also some of the
debugging messages on the terminal (a run-time switch can make different
outputs go to the file or to the tstream).
If I spent a long time poring over the manuals, I could probably figure
out how to do this right, but I thought I would first ask if anyone else
has done this, or anything similar enough to provide either a cookbook
approach or some hints on what to avoid or worry about.
I managed to get something similar working by deriving a type
tfilebuf from filebuf, and a type tstreambase from ios, containing a
tstreambuf, then to derive tstream from ostream and tstreambase.
ios
filebuf |
| Tstreambase ostream
Tfilebuf \ /
Tstream
However, this was aesthetically unpleasing, and less general than I
would have liked. I also only redefined the overflow function; I am a
little concerned that some of the other virtual functions might also get
called at some point.
Clearly, in order to get the functionality of an ostream, I need to
inherit from ostream, but since an ostream has data, I can only contain
one ostream; I now have an assymetry between the two ostreams which are
being printed to: one is a base class and one is a member. Also, why
don't all the output functions defined on ostream resolve to calling a
single function, like "put"? That surely would have made inheriting
from ostream much simpler.
I confess to not understanding the workings of streams fully, and would
appreciate a brief, higher level description of how to derive from them
than is presented by the man pages.
Thanks.
Jak jak@cs.brown.edu
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I wouldn't recommend sex, drugs or insanity for everyone, but they've
always worked for me.
-- Hunter S. Thompson
amb@Apple.COM (A. Michael Burbidge) (01/31/91)
In article <JAK.91Jan31095444@bimini.cs.brown.edu>, jak@cs.brown.edu (Jak Kirman) writes: > I would like to create a type which behaves precisely as does an ostream > except that the constructor would take two ostream references, and every > operation would affect both ostreams equally. ... > > Clearly, in order to get the functionality of an ostream, I need to > inherit from ostream, but since an ostream has data, I can only contain > one ostream; I now have an assymetry between the two ostreams which are > being printed to: one is a base class and one is a member. Also, why > don't all the output functions defined on ostream resolve to calling a > single function, like "put"? That surely would have made inheriting > from ostream much simpler. The ostream class is practically useless as a base class since all of the output operators are non-virtual. I have asked on this newsgroup before why aren't the output operators virtual? I received no answers. Mike Burbidge Apple Computer, Inc. the output operators virtual. I got no answers.
dfoster@jarthur.Claremont.EDU (Derek R. Foster) (02/01/91)
In article <JAK.91Jan31095444@bimini.cs.brown.edu> jak@cs.brown.edu writes: >I would like to create a type which behaves precisely as does an ostream >except that the constructor would take two ostream references, and every >operation would affect both ostreams equally. ... >out how to do this right, but I thought I would first ask if anyone else >has done this, or anything similar enough to provide either a cookbook >approach or some hints on what to avoid or worry about. Well, I fought long and hard with the TC++ iostream.h header file, and I eventually gained some understanding of it, albeit sketchy in a few places. My goal was to create an ostream type that would output to the console device, like cprintf, so I could, for instance, write con << clear << fore(LIGHTGREEN) << "Green text" << back(LIGHTGRAY) << "Gray text"; I eventually succeeded in this, and although my result is more than a little shaky, (I guessed at what some of the member functions did based on their names and their return values, since TC++ thoughtfully failed to provide documentation. Sort of risky.), It seems to work fine for all the code I've tried it with. I could make this code available if anyone is interested. What you want to do is much easier. Keep this in mind: an ostream doesn't actually do output itself. Instead, it formats the output and then tells the streambuf associated with it to deal with the output. Thus, if all you want to do is create an ostream-like object to output to two different streams, you don't need to derive from ostream at all. What you need to do is create a streambuf object which will output to two destinations instead of one. Then, you could simply associate this new streambuf with a normal ostream, or, if you really want to be able to use a fancy constructor as you've described, with a class derived from ostream which simply adds the new constructor, and uses it to initialize itself as an ostream with one of your new streambufs instead the normal one it would usually get. One way would be to have your derived streambuf contain pointers to two other more normal streambufs. Then you could just define the relevant member functions of the derived streambuf to call the same functions in each of the pointed-to streambufs. (Your derived streambuf can even be unbuffered, if you like, since its buffer won't be used for anything.) I.e. to use this, you might eventually be able to do something like: #include <fstream.h> class mystreambuf : public streambuf { streambuf * A; streambuf * B; mystreambuf(streambuf* a, streambuf* b) {A=a;B=b;unbuffered(1);); int overflow(int = EOF) {A->overflow(); B->overflow();}; // probably need to redefine all the virtual output functions, like // do_sputn() etc. in the same way. I'm not sure what to do about // return values. (Probably will have to vary with the function.) } filebuf buf1("Out1.dat"); filebuf buf2("Out2.dat"); mystreambuf x(strbuf1, strbuf2); ostream y(x); y << "Testing 1.2.3..."; If you're really serious about the constructor being a constructor from two ostreams, you might try something like: // define mystreambuf as before class tstream : public ostream { tstream(ostream& A, ostream& B) : ostream(new mystreambuf(A.rdbuf(),B.rdbuf()) {}; } ofstream file1("abc.dat"); ofstream file2("def.dat"); tstream both(file1, file2); both << "Testing 4..5..6"; note 1: A.rdbuf() is simply the value of A's pointer to the streambuf that it expects to output on. note 2: This creates a semi-permanent tie between the buffers of the two original streams and the buffer of the tstream. If you go mucking about with the buffers of the original streams, you will have an effect on the tstream. This could be good or bad depending on what you do. I hope this helps! Derek Riippa Foster
mat@mole-end.UUCP (Mark A Terribile) (02/03/91)
> > Clearly, in order to get the functionality of an ostream, I need to > > inherit from ostream, but since an ostream has data, I can only contain > > one ostream; I now have an assymetry between the two ostreams which are > > being printed to: one is a base class and one is a member. Also, why > > don't all the output functions defined on ostream resolve to calling a > > single function, like "put"? That surely would have made inheriting > > from ostream much simpler. > > The ostream class is practically useless as a base class since all of the output > operators are non-virtual. I have asked on this newsgroup before why aren't > the output operators virtual? I received no answers. First, the operators are not virtual because it would have imposed the cost of virtual function call on every character, defeating the benefits of bufferring the output. Realize that there are two different categories of output on ostream s. Output via the insert operator is ``formatted,'' output via the functions is ``unformatted.'' They former is subject to a variety of control flags and other goodies; the latter is not. The key point in the stream is its interface to the streambuf. I certainly don't claim that it's intelligible; only that you need to do your magic stuff BENEATH that interface and inside the streambuf. What you need is a streambuf whose flush function sends its output to two other streambufs, those other streambufs representing where you want the output to go. It's the streambuf, not the stream, from which you need to derive. -- (This man's opinions are his own.) From mole-end Mark Terribile