Help with operator overloading of <<

W

winbatch

Hi,
I'm trying to play with classes and I'm having trouble with a
theoretical
problem.

I've create a 'Log' class, who creates and writes to log files. I was
able
to overload the << operator so that I could write to my log as follows:

Log logOne("log.txt", true, "\n" );
logOne<<"BLAH"<<"BLAH2";

In the constructor, the 'true' refers to having the log class write out
a
datetime stamp of each log entry, while the '\n' indicates how to
terminate
the log line.

The problem is that when I use the syntax about (<<"A"<<"B" ), etc, it
ends
up writing multiple lines because the << function is getting called
each
time. What I would like to do is have A and B be concatenated into one
string and then do the actual call to write the log line once. How do I
know
if I am the 'LAST' item being sent to the << so that I can store the
intermediate values and not write it out unless I am last?

Here is my function: (quite simple)
Log & operator << (string a)
{
writeLog(a);
return *this;
}


Thanks in advance...
 
C

Chris Theis

winbatch said:
Hi,
I'm trying to play with classes and I'm having trouble with a
theoretical
problem.

I've create a 'Log' class, who creates and writes to log files. I was
able
to overload the << operator so that I could write to my log as follows:

Log logOne("log.txt", true, "\n" );
logOne<<"BLAH"<<"BLAH2";

In the constructor, the 'true' refers to having the log class write out
a
datetime stamp of each log entry, while the '\n' indicates how to
terminate
the log line.

The problem is that when I use the syntax about (<<"A"<<"B" ), etc, it
ends
up writing multiple lines because the << function is getting called
each
time.

Operator << must (!) be called here twice because you call it twice ;-) So
that behavior absolutely makes sense.
What I would like to do is have A and B be concatenated into one
string and then do the actual call to write the log line once. How do I
know
if I am the 'LAST' item being sent to the << so that I can store the
intermediate values and not write it out unless I am last?

Here is my function: (quite simple)
Log & operator << (string a)
{
writeLog(a);
return *this;
}

Without seeing the writeLog(a) function this code snippet is not worth very
much, but there are some things that are visible right away. First of all
you should pass a const string reference instead of the string object. This
has nothing to do with your problem but you´ll save yourself unnecessary
copies of the string object.
Despite this your problem is located in the writeLog function which I assume
will look something like this:

void writeLog( string Text ) {
cout << m_Date << " " << Text << endl;
}

And here it is - the end of line manipulator endl. Just skip it and all
strings are going to be output on the same line. However, you should
terminate the line yourself sending the endl or "\n" to the stream after the
last thing you want to output. In principle it works just like the normal
cout stream.

Cheers
Chris
 
D

Dan Hoffman

Chris,

I don't have an endl in my writeLog function. The reason is that the
constructor takes a parameter which will be the EOL for the log. The reason
for this is that I have another class which contains multiple logs and I
just send it a string and it will call the writeLog of each of the Log
objects inside. Since certain logs will have different terminators, I
didn't want to have to deal with this when I passed the string to be logged,
but rather it be part of the Log object itself.

I realize that the << is being called twice, and that's exactly the problem.
I only want the log to be written to the file when I'm on the LAST call to
the <<. All previous calls should simply append the string to some buffer
so that when I hit the last call to <<, I can write out my buffer (with the
last string included).

This is the writeLog function for your reference. Regarding your advice
about const, I have taken your advice.

void Log::writeLog( const string &logLine )
{
content=logLine;
if ( !out->is_open() )
throw DHException( 3, "Attempt to write to log file [", fileName.c_str(),
"] without opening first!");

if ( includeDate )
{
current.now();
content = Date::getFormattedDate( current ) +">" + content + suffix;
}


out->write( content.c_str(), content.size() );
out->flush();

if ( !out->is_open() )
throw DHException ( 3, "Could not write to log file [", fileName.c_str(),
"] !");

}
 
G

GB

winbatch said:
I've create a 'Log' class, who creates and writes to log files. I was
able
to overload the << operator so that I could write to my log as follows:

Log logOne("log.txt", true, "\n" );
logOne<<"BLAH"<<"BLAH2";

In the constructor, the 'true' refers to having the log class write out
a
datetime stamp of each log entry, while the '\n' indicates how to
terminate
the log line.

The problem is that when I use the syntax about (<<"A"<<"B" ), etc, it
ends
up writing multiple lines because the << function is getting called
each
time. What I would like to do is have A and B be concatenated into one
string and then do the actual call to write the log line once. How do I
know
if I am the 'LAST' item being sent to the << so that I can store the
intermediate values and not write it out unless I am last?

Instead of writing the timestamp from within the insertion function,
require that it be added explicitly. You can do this by defining your
own timestamp stream manipulator, logstamp, which you would then use
like this:

log << logstamp << "blah1" << "blah2" << std::endl;


I have done something similar to this. Another thing I have done to make
the log thread-safe is to take advantage of the C++ property that
temporary objects are guaranteed not to be destroyed until the entire
expression in which it appears is evaluated. I take advantage of this by
acquiring a log mutex in logstamp's constructor and releasing it in its
destructor. That way the entire string of insertions is guaranteed to be
atomic. No need for printf-style output mechanism. Details are
off-topic, though.

Gregg
 
C

Chris Theis

Dan Hoffman said:
Chris,

I don't have an endl in my writeLog function. The reason is that the
constructor takes a parameter which will be the EOL for the log. The reason
for this is that I have another class which contains multiple logs and I
just send it a string and it will call the writeLog of each of the Log
objects inside. Since certain logs will have different terminators, I
didn't want to have to deal with this when I passed the string to be logged,
but rather it be part of the Log object itself.

I realize that the << is being called twice, and that's exactly the problem.
I only want the log to be written to the file when I'm on the LAST call to
the <<. All previous calls should simply append the string to some buffer
so that when I hit the last call to <<, I can write out my buffer (with the
last string included).

This is the writeLog function for your reference. Regarding your advice
about const, I have taken your advice.

void Log::writeLog( const string &logLine )
{
content=logLine;
if ( !out->is_open() )
throw DHException( 3, "Attempt to write to log file [", fileName.c_str(),
"] without opening first!");

if ( includeDate )
{
current.now();
content = Date::getFormattedDate( current ) +">" + content + suffix;
}


out->write( content.c_str(), content.size() );
out->flush();

if ( !out->is_open() )
throw DHException ( 3, "Could not write to log file [", fileName.c_str(),
"] !");

}

Chris Theis said:
Operator << must (!) be called here twice because you call it twice ;-) So
that behavior absolutely makes sense.

Hi Dan,

I personally would find it clearer to implement the date/time stamp as a
manipulator as already recommended in another posting. Otherwise you could
implement your log to buffer the strings until your own user supplied endl -
manipulator signals a flush.

Cheers
Chris
 

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

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top