• We’re currently investigating an issue related to the forum theme and styling that is impacting page layout and visual formatting. The problem has been identified, and we are actively working on a resolution. There is no impact to user data or functionality, this is strictly a front-end display issue. We’ll post an update once the fix has been deployed. Thanks for your patience while we get this sorted.

C++ Operator<< woes

Replicon

Member
Hey, I figured someone here might know this:

I'm trying to implement a very simple logger that does two things:

- log levels
- outputs log to as many streams as I want it to.

Roughly speaking, the usage looks something like:

Logger log(LogLevel::WARNING);
log.attachStream(std::cerr); // and any others, say file handles....

log << LogLevel::ERROR << "uh-oh, " << 75 << " errors occured...\n";

Currently, I don't yet have the LogLevel stuff working. So if we ignore the loglevel, it's pretty straight forward:

template< typename T >
Logger &operator<<(Logger &logger, const T &x)

Then, just keep shoving what you get into all the streams.

But right off the bat, I am seeing an annoyance: I'd like to be able to call flush() on each of my output streams ONCE per "line of code" (for the lack of a better way of saying it). In other words, for something like:

log << 1 << 2 << 3 << 4 << 5;

I want to call flush() just once, after the whole "12345" has been shoved into my streams. Is there a way to do this without having to write a Logger::flush and call it manually? I'm thinking 'no'. And I'm thinking it probably doesn't matter (depending on implementation of flush I guess), but it's worth a shot hehe.

Secondly, I'm not really sure how I'll put the log levels in there. Should I just change the operator<< above to have these two instead:

template< typename T >
LogLevel &operator<<(LogLevel &logLevel, const T &x)

Logger &operator<<(Logger &logger, LogLevel &logLevel);

and then have the loglevel one maintain an ostringstream, and have the Logger's operator<< look at logLevel and decide whether to publish the data? The problem I forsee there is that there's probably no guarantee on order of execution, so instead of the desirable:

operator<<(log, operator<<(LogLevel::WARNING, "test"));

It might try something goofy like:

operator<<( operator<<(log, LogLevel::WARNING), "test" );

Which would not even compile...

Thoughts?
 
Look at the standard stream implementation for a solution to both problems. cout << endl flushes and you can also "output" various iomanip functions (e.g. hex) to modify the output. I believe execution from left to right is guaranteed.

Looking at this: http://www.cplusplus.com/refer...manipulators/endl.html
and this: http://www.cplusplus.com/refer...am/operator%3C%3C.html
it looks like the manipulators are defined as functions and the <<operator takes in the function pointer. I'm sure there are other ways of doing it too like defining an enum, or using global functor objects.
 
Thanks for the links. I can see how the manipulators are done, but the problem is that they leave the stream manipulated, and I want to disallow that. In particular, I don't want to allow this:

log << "haha, I didn't put a loglevel!\n";

But I guess I can live without it, and all the other problems would go away hehe.
 
<div class="FTQUOTE"><begin quote>Originally posted by: Replicon
Thanks for the links. I can see how the manipulators are done, but the problem is that they leave the stream manipulated, and I want to disallow that. In particular, I don't want to allow this:

<div class="FTQUOTE"><begin quote>
log << "haha, I didn't put a loglevel!\n";
</end quote></div>

But I guess I can live without it, and all the other problems would go away hehe.</end quote></div>

perhaps one thing you could do, is to return a modfied, separate logger object when the << operator encounters a manipulator. since all the other operations down the chain use the same object you would get persistent modification, for that statement only.
 
That's an interesting idea. As an added bonus, I could have that different object do the stream-flushing in its destructor, if I can get it to live exactly the length of the call 🙂.
 
Indeed, it does. Maybe I should just make it support std::endl, for consistency's sake, and just force them to use that.
 
Back
Top