Implementing operator<<

S

soy.hohe

Hi all

I have a class StreamLogger which implements operator << in this way:

template <class T> StreamLogger& operator<<(const T& t)
{
<print the stuff using fstream, etc.>

return *this;
}

It works perfectly, using it like:
m_logger << "My ID is " << getID() << "\n";

but for some reason it does not compile this code:
m_logger << std::endl;

It gives the following error:
--------------------------------------------------------------------------------
..\src\Dealer.cpp(70) : error C2678: binary '<<' : no operator found
which takes a left-hand operand of type 'StreamLogger' (or there is no
acceptable conversion)
C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\include
\afx.h(1428): could be 'CDumpContext &operator <<(CDumpContext
&,ATL::CTimeSpan)'
....
--------------------------------------------------------------------------------

Could you guys help me understand why it doesn't compile? (because I
dont have a clue...)


Thanks!!!

Jorge
 
O

Ondra Holub

Hi all

I have a class StreamLogger which implements operator << in this way:

template <class T> StreamLogger& operator<<(const T& t)
{
<print the stuff using fstream, etc.>

return *this;

}

It works perfectly, using it like:
m_logger << "My ID is " << getID() << "\n";

but for some reason it does not compile this code:
m_logger << std::endl;

It gives the following error:
--------------------------------------------------------------------------------
.\src\Dealer.cpp(70) : error C2678: binary '<<' : no operator found
which takes a left-hand operand of type 'StreamLogger' (or there is no
acceptable conversion)
C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\include
\afx.h(1428): could be 'CDumpContext &operator <<(CDumpContext
&,ATL::CTimeSpan)'
...
--------------------------------------------------------------------------------

Could you guys help me understand why it doesn't compile? (because I
dont have a clue...)

Thanks!!!

Jorge


You have to do following steps:

1. Create your own manipulator in std:
namespace std
{
StreamLogger& endl(StreamLogger& s)
{
// Here do what manipulator should do
return s;
}

StreamLogger& hex(StreamLogger& s)
{
// Here do what manipulator should do
return s;
}

// Define all standard manipulators which do not have parameters
// ...
}

2. Overload operator<< to accept function of the same type as
manipulator is:
StreamLogger& operator<<(StreamLogger& s, StreamLogger& (*manip)
(StreamLogger& s))
{
return manip(s);
}

3. Use your manipulator in usual way:
StreamLogger sl;
sl << std::endl;

You should define all standard manipulators for your StreamLogger to
have standard interface. Do not ask me how to support anipulators with
parameters. I know it is (of course) possible, but I do not remember
the solution.
 
J

James Kanze

I have a class StreamLogger which implements operator << in this way:
template <class T> StreamLogger& operator<<(const T& t)
{
<print the stuff using fstream, etc.>
return *this;
}
It works perfectly, using it like:
m_logger << "My ID is " << getID() << "\n";
but for some reason it does not compile this code:
m_logger << std::endl;

The problem is that std::endl is also a template. The actual
instantiation of the template will be determined by the type of
the parameter of operator<<, and the type of the parameter of
operator<< will be determined by the type of the argument, i.e.
the type of the instantiated std::endl template.

You'll run into this problem any time you try to output
something that is an uninstantiated template (or an overloaded
function)---in practice, that means the standard manipulators.
The solution is to add a non-template overload to catch
them---template argument deduction fails for your function
template, but the non-template overloads are still considered.
Something like:

inline OutputStreamWrapper const&
operator<<(
OutputStreamWrapper const&
dest,
std::ios_base& (* manip)( std::ios_base& ) )
{
std::eek:stream* stream = dest.stream() ;
if ( stream != NULL ) {
*stream << manip ;
}
return dest ;
}

inline OutputStreamWrapper const&
operator<<(
OutputStreamWrapper const&
dest,
std::eek:stream& (* manip)( std::eek:stream& ) )
{
std::eek:stream* stream = dest.stream() ;
if ( stream != NULL ) {
*stream << manip ;
}
return dest ;
}

seems to be sufficient. (I note that in my code, I've also got
a non-template overload for char const*. I don't remember why;
whether there is some fundamental problem due to the actual type
of the string literal being char const[], or if it is just a
work-around for a compiler bug somewhere. In general, however:
most of the time you encounter a problem, it will be because
template argument deduction isn't able to handle the case, and
an explicit non-template overload will do the trick.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,060
Latest member
BuyKetozenseACV

Latest Threads

Top