Sign in to follow this  

Custom subclass of std::ostream for custom printing?

This topic is 2087 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Suppose I have some custom objects that I want to print to various targets.

I have a function that prints them to an ostream:

[code]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;
}[/code]

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 [url="http://stackoverflow.com/questions/4366904/custom-stream-to-method-in-c"]this post[/url], 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...

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

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? Edited by shurcool

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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! [img]http://public.gamedev.net//public/style_emoticons/default/biggrin.png[/img] 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.

Share this post


Link to post
Share on other sites
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:

[code]void PrintObject(Object & obj, GLStream & out)[/code]

As soon as I changed it to:

[code]void PrintObject(Object & obj, ostream & out)[/code]

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?

Share this post


Link to post
Share on other sites
I'm confused.

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

[code]struct Foo
{
int Member;
};

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


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:

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

Share this post


Link to post
Share on other sites
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):

[code]stream << foo.Member;[/code]

I'd like to do something like this:

[code]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)[/code]

Is it impossible, short of using macros (which I don't wanna do for obvious reasons)? Edited by shurcool

Share this post


Link to post
Share on other sites
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 [url="http://www.codeproject.com/Articles/2630/Deriving-your-own-stream-from-the-iostreams-framew"]here[/url]. 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.

Share this post


Link to post
Share on other sites
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:

[code]streamsize xsputn ( const char * s, streamsize n );[/code]
[url="http://www.cplusplus.com/reference/iostream/streambuf/xsputn/"]http://www.cplusplus...reambuf/xsputn/[/url]

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 [u][b]impossible in C++[/b][/u] (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.

Share this post


Link to post
Share on other sites
Ahhh, now I see what you're after.


A templated free function should do the trick:

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

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 [url="http://code.google.com/p/epoch-language/source/browse/Shared/User%20Interface/OutputStream.inl"]here[/url].

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Woohoo, I've got everything to work exactly how I wanted. Thanks so much ApochPiQ for suggesting templates and for useful code on stream manipulators (I just learned that std::endl is actually a function, not a constant, hehe)! I would've given up on this without your help.

It ended up looking something like this:

[code]class MyObject
{
// ...

// This takes care of printing MyObject to any stream (std::ostream, OpenGLStream)
template <typename StreamT> friend StreamT & operator << (StreamT & out, const MyObject & obj)
{
out << obj.A;
out << "(" << obj.B << ")" << endl;
// ...

return out;
}

private:
MySubObject A;
MySubObject B;
// ...
}[/code]

[code]class MySubObject
{
// ...

// This takes care of printing the MySubObject to a text-based std::ostream
friend std::ostream & operator << (std::ostream & out, const MySubObject & subobj)
{
out << subobj.ToText();

return out;
}
}[/code]

[code]#pragma once
#ifndef __OpenGLStream_H__
#define __OpenGLStream_H__

class OpenGLStream
{
public:
OpenGLStream(Vector2n CaretPosition);
~OpenGLStream();

// This takes care of drawing the MySubObject using OpenGLStream
OpenGLStream & operator << (const MySubObject & MySubObject)
{
// Render MySubObject on screen using extra info within the sub-object
}
OpenGLStream & operator << (const std::string & String);
OpenGLStream & operator << (OpenGLStream & (*f)(OpenGLStream &));

void NewLine();

private:
OpenGLStream(const OpenGLStream &);
OpenGLStream & operator =(const OpenGLStream &);

Vector2n m_CaretPosition;
sint32 m_LineStartX;
};

OpenGLStream & endl(OpenGLStream & stream);

#endif // __OpenGLStream_H__[/code]

[code]// And I'm able to do this:
OpenGLStream OpenGLStream(f.Position);
OpenGLStream << f;

// As well as:
std::ofstream File("SomeFile.txt");
File << f;[/code]

It's beautiful.

Share this post


Link to post
Share on other sites
No, really, it's wrong.

The [font=courier new,courier,monospace]std::ostream[/font] (and variants) is a formatter, it converts your data into 'text' (for some definition of 'text') for insertion into some output stream. The insertion into an output stream is done through the transport calss, the [font=courier new,courier,monospace]std::ostreambuf[/font] (and variants).

Your [font=courier new,courier,monospace]operator<<()[/font] is all right. The provision of [font=courier new,courier,monospace]OpenGLStream[/font], a transport class, as a replacement for [font=courier new,courier,monospace]std::ostream[/font], a formatter class, will end up making things difficult and awkward. You will realize this if you try to do regular C++ output. It will not work the way you expect.

I really strongly suggest you read up on [font=courier new,courier,monospace]std::streambuf[/font] and implement your transport class as a streambuf. You provide one ostream operator, you write the same way to any ostream. Your transport knows how to do the transpot.

The secret magic trick is that you write your own manipulators. Like the standard manipulators ([font=courier new,courier,monospace]std::setw[/font], [font=courier new,courier,monospace]std::endl[/font], and so on), your manipulators affect the final output and get called inband in your [font=courier new,courier,monospace]operator<<([/font]). Internally, each manipulator can check the type of ostream they are operating on and can ignore those that don't apply. For example, your could have a [font=courier new,courier,monospace]setcolor(Color)[/font] manipulator that works with your (hypothetical) [font=courier new,courier,monospace]OpenGLStreambuf[/font] class to set the [font=courier new,courier,monospace]v_color[/font] varying in your shader. Your [font=courier new,courier,monospace]operator<<[/font] might look like this.[code]
friend std::ostream& operator<<(std::ostream& ostr, const MyObject& obj)
{
out << obj.A;
for (int i = 0; i < obj.B.size(); ++i)
{
out << i << ": " << setcolor(Color::blue) << '(' << obj.B.at(i) << ')' << setcolor(Color::black) << '\n';
}

// ...
return ostr;
}[/code]
And that should work correctly with any stringstream or filestream, whether it's garden-variety or using your OpenGLStreambuf[font=courier new,courier,monospace].[/font]

Of course, if what you have is working for you, fine. But if you start running into anoying conversion problems, think about using the streambuf approach. It's how the standard library was designed to be used.

Share this post


Link to post
Share on other sites
Bregma, thanks a lot for your insight.

I figured that my solution is kinda a fancy hack. One side-effect is that I can only work with printing "endl" to my streams (via using std::endl; ), because that name is ambiguous and works for both std::endl and my own endl(OpenGLStream &). If I print std::endl, then it obviously breaks.

However, what I'm working on is an in-progress prototype that is rapidly evolving in ways I cannot predict. I don't want to be limited at this time by having to channel my rendering of MyObjects via custom manipulators. I really just want to have a function that accepts (MyObject &) and does whatever arbitrary code I write. So for the time being, I'll stick with my current solution because it does exactly what I need and allows me to easily change things.

Share this post


Link to post
Share on other sites

This topic is 2087 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this