The Joel on Software Discussion Group (CLOSED)

A place to discuss Joel on Software. Now closed.

This community works best when people use their real names. Please register for a free account.

Other Groups:
Joel on Software
Business of Software
Design of Software (CLOSED)
.NET Questions (CLOSED)
TechInterview.org
CityDesk
FogBugz
Fog Creek Copilot


The Old Forum


Your hosts:
Albert D. Kallal
Li-Fan Chen
Stephen Jones

C++: Overloading of operator<<

Hi,

I'm having a particularly slow day and can't get my head round an operator overloading problem. I would like a class to be able to accept data via an insertation operator, i.e:

myClassInstance << std::string("a string") << 4 << 3.4 << std::endl;

Internally, I'd like everything to end up in a stringstream so I can then farm it off to other streams (say std::cout and an ofstream). I have got horribly confused how I can do this without having to write an operator<< overload for every data type, and how an input stream would be created on the first call (myClassInstance << ...).

Any help gratefully received!
Brian
Friday, February 15, 2008
 
 
Perhaps you can declare an implicit type conversion operator in your class, and define this operator as returning a reference to your stringstream instance .. so that your class can be used wherever a stringstream can be used.
Christopher Wells Send private email
Friday, February 15, 2008
 
 
Hi Brian,

> I have got horribly confused how I can do this
> without having to write an operator<< overload
> for every data type

Normally this kind of thing is done by using a template member function. It kind of gives you a wildcard match to allow any data type to be passed in and handled in the same way.

Here is an example that I think does what you are asking about - this was tested with Visual C++ 7.1 :

#include <iostream>
#include <sstream>
#include <memory>

using namespace std;

class MyClass
{
    auto_ptr<stringstream> m_spStm;

public:
    MyClass()
    {
    }

    ~MyClass()
    {
    }

    void WriteToStream( ostream& ostm )
    {
        if ( m_spStm.get() )
            ostm << m_spStm->str();
    }

    template<typename T>
    ostream& operator<<( T& ObjToWrite )
    {
        // Create stringstream if not done previously.
        if ( m_spStm.get() == NULL )
            m_spStm.reset( new stringstream );

        // Write object to stringstream.
        (*m_spStm) << ObjToWrite;

        // Return stringstream to allow operator<< chaining.
        return (*m_spStm);
    }
};

int main()
{
    MyClass x;

    x << std::string("a string") << 4 << 3.4 << std::endl;

    x.WriteToStream( cout );
}
Michael G
Friday, February 15, 2008
 
 
I think it's more common to have operator<< return *this, so that subsequent << calls in the chain go through the object's operator and not the member's.
Brian McKeever Send private email
Friday, February 15, 2008
 
 
Brian,

I think you are approaching this the wrong way. I don't think you don't want to overload the << operator, you want to make your class behave like an output stream or an output stream buffer.

From what you say, it looks like you are trying to duplicate an output stream both to standard out and also to a log file. For that you may wish to look at how to overload the basic_streambuf or basic_filebuf class with a modified version of your own.

Take a look at this page, it may be useful: http://www.cs.technion.ac.il/~imaman/programs/teestream.html

"how an input stream would be created on the first call (myClassInstance << ...)"

This is not an input stream, this is an output stream. You are sending output to myClassInstance with the << operator. ">>" is the input operator. The output stream would be created either in the constructor when myClassInstance is created, or in a later initialization method.

If you really want strings, then you could take a look at how basic_ostringstream and basic_stringbuf are implemented, and then follow the same design pattern either for "myClass_ostream" and/or "myClass_streambuf".
Ian Boys Send private email
Friday, February 15, 2008
 
 
"I don't think you want to..."
Ian Boys Send private email
Friday, February 15, 2008
 
 
Input stream, output stream, lack of sleep clarity stream :D

Thanks for the advice all. The template member function looks  the best bet for what I want (rather than making the object an output stream itself; I did look at this but it isn't the correct solution given other things I want the class to do).

Thanks all.

James.
Brian
Sunday, February 17, 2008
 
 
So I've implemented something similar to the idea above, which looks something like this:

------------------------------------
    template <typename T> EventLogger& operator <<(const T &toWrite)
    {
        // Write object to std out
        if(_useStdOut)
        {
            std::cout << toWrite;
        }
        
        // Write object to file
        if(_useFile)
        {
            *_outFileStream << toWrite;
        }
        
        // Return reference to this to allow operator chaining
        return *this;
    }
------------------------------------

This works fine until you try to pass std::endl into it. I'm guessing I need a template specialisation to handle this, but can't find anywhere what the type of std::endl is to create one (the GCC includes are a bit of a headache to try and track this through!). Can anyone suggest how to handle this correctly?
Brian
Sunday, February 17, 2008
 
 
Hi Brian,

> Can anyone suggest how to handle this correctly?

Well, the easiest way is to just return the ostream from your member template function like in my original example. That way it handles all this stuff for you.

But if you do want to do it where you don't return the ostream, you will have to define an additional operator<< that takes a function pointer, like this:

    MyClass& operator<<( ostream& (*pfn)(ostream& stm) )
    {
        // Call manipulator function on ostream.
        pfn( *m_spStm );

        return (*this);
    }

But there are also a couple other variants of manipulators like ios manipulators, so you would also need to provide some additional operator<< for those as well. So it is more annoying to do it this way than just returning an ostream.
Michael G
Sunday, February 17, 2008
 
 
I see no obvious reason for not just using a stringstream and then processing its content later. What do you gain by making an object pretend to be a stream when it isn't?
Ian Boys Send private email
Sunday, February 17, 2008
 
 
Ian:

Semantics. This is an event logger, a singleton, and needs to accept anything you throw into it that could be put into a 'normal' stream.
JJ Send private email
Monday, February 18, 2008
 
 
Hi,

The correct approach consists in:
- specializing a std::streambuf for your I/O "channel"
- specializing a std::*stream to simplify the use of your stream (e.g. initialization aspects, ...) (this is not mandatory)

See http://www.angelikalanger.com/Articles/C++Report/IOStreamsDerivation/IOStreamsDerivation.html
Luc Hermitte Send private email
Monday, February 18, 2008
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz