Custom subclass of std::ostream for custom printing?

Started by
11 comments, last by shurcool 12 years ago
Suppose I have some custom objects that I want to print to various targets.

I have a function that prints them to an ostream:

void PrintObject(Object & obj, ostream & out)
{
out << ">";
PrintSubObject(obj.customSubObject1, out); // I'd like to replace this with: out << obj.customSubObject1;
out << "(";
PrintSubObject(obj.customSubObject2, out); // I'd like to replace this with: out << obj.customSubObject2;
out << ")";
out << endl;
}


This works great for printing to anything that's a subclass of ostream, for example files (ofstream) or string streams (ostringstream).

Now, suppose I want to print the same objects to another target, for example to the screen via an OpenGL window and a function that renders text on screen.

Would it be a good idea to do this by deriving a class from std::ostream and using that approach, or is it wrong in some way? I'd like to avoid having a DrawObject() function that has the same code, except out is an OpenGLTextPrinterStream instance.

I've tried doing it, but it's not easy. Just overriding the operator<<() functions in my ostream subclass doesn't work, because they're not virtual functions and never get called.

Based on this post, it seems a proper approach would be to use a custom streambuf subclass. Is this what I should be doing?

When I take that approach, my problem is that streambuf is really designed to work with character-based streams, but what I really want to do is process the entire object at once, rather than having to break it down into characters and then re-assembling it.

It'd be great if I could just do this...

class OpenGLTextPrinterStream {
operator << (CustomSubObject subobj)
{
// Render CustomSubObject on screen
}
}


Any suggestions? Am I shoehorning ostream into something it shouldn't be used for, or if this is indeed a good approach, how do I go about implementing it?
Advertisement
The only reason I can think of that you can't use the code you posted at the end would be that you should be passing subobj by const reference and not value. Other than that, I see no impediment to using that kind of code as a drop-in replacement for ostreams.

I've done something similar for supporting custom colorization manipulators in a thin wrapper around std::cout.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Wow, you're absolutely right. That's why it didn't work when I tried it earlier, I was missing the const reference part. My understanding of when virtual/overloaded functions get called is quite rusty.

Thanks so much! biggrin.png Now I can have a very nice implementation.

P.S. Coincidentally, I also need this to do some colourization/styling of text output. That's why I couldn't just dump everything to a string and work with it that way: then I lose the extra information of where various types of elements begin/end.
Darn, I think I might've jumped the gun on this.

The reason it worked is because I accidentally had the subclass as my function parameter:

void PrintObject(Object & obj, GLStream & out)

As soon as I changed it to:

void PrintObject(Object & obj, ostream & out)

The overridden GLStream::operator << (const SubObject &) function is no longer getting called. Which is what I expected because the << operator was not declared to be virtual in std::ostream, right?
I'm confused.

What I meant by my post is that this code should work:

struct Foo
{
int Member;
};

struct Outputter
{
Outputter& operator << (const Foo& foo)
{
std::cout << foo.Member << std::endl;
return *this;
}
};



Trying to override an operator in ostream is not going to work because as you note operators are not virtual.

If you just want to output a Foo to something that is an ostream, you should use a free function:

std::ostream& operator << (std::ostream& stream, const Foo& foo)
{
stream << foo.Member;
return stream;
}

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

So my goal is to output Foo both to an ostream, and also to something else that's not (necessarily) an ostream, _while_ not repeating the code that does the outputting twice (because it's pretty lengthy).

From your example, I don't want to repeat this code twice (once for printing to an ostream and the other for drawing to screen using OpenGL):

stream << foo.Member;

I'd like to do something like this:

CustomObject MyObject;

std::ofstream File;
OpenGLTextRenderingStream MyOpenGLTextRenderingStream;

operator << (??? stream, CustomObject obj)
{
// Lots and lots of lines of code that output to stream...
stream << obj.a;
stream << obj.b;
stream << obj.c;
// ...
}

class OpenGLTextRenderingStream {
operator << (std::string s)
{
// Draw string s on the screen using OpenGL
}
operator << (CustomSubObject subobj)
{
// Draw subobj on the screen using OpenGL
}
}

File << MyObject; // Output MyObject as text into a file; I already have this behaviour happening in my program
MyOpenGLTextRenderingStream << MyObject; // This is what I'd like to support:
// render MyObject on the screen using colour/style info
// which MyObject contains (it gets ignored when outputting to a file)


Is it impossible, short of using macros (which I don't wanna do for obvious reasons)?
I think there may be a ray of hope by pursuing a custom streambuf approach - I don't see any other way. I've found some relevant info here. I'm looking into this now.

I've figured it should be possible via streambuf, but I had a hard time figuring out how to make it work on a more object-at-a-time basis rather than character-at-a-time. I hope I can figure it out.
Ok, I don't think stringbuf will work either.

In the best case, the most "object-at-a-time" that it'll let me work with, AFAIU, is via this function:

streamsize xsputn ( const char * s, streamsize n );
http://www.cplusplus...reambuf/xsputn/

Which basically gives me a whole bunch of chars at a time, which is better than single-char-at-a-time, but still too late because I lose access to my per-object colour/style information.

Therefore, unless I'm missing something, I declare my specific task of not duplicating code to output a custom object both to an std::ofstream and another custom stream class to be impossible in C++ (under the assumption of not using macros, and not modifying the source code of standard library).

I guess I'll have to settle for a not so nice looking solution and duplicate that code.
Ahhh, now I see what you're after.


A templated free function should do the trick:

template <typename StreamT>
StreamT& operator << (StreamT& stream, const Foo& foo)
{
stream << foo.Member;
return stream;
}


Then define some custom stream manipulators that affect StreamT when it is an OpenGL rendering stream and do nothing when applied to a std::ostream.

You can see an example of custom stream manipulators here.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Ah, yes, templates. How could I not think of them... >.< It seems so obvious in hindsight (especially since I was thinking of macros).

Thanks, I'll try what you said! Things are looking up once again hehe.

This topic is closed to new replies.

Advertisement