So I have such log class:
#include <iostream>
#include <sstream>
#include <boost/circular_buffer.hpp>
#include <boost/foreach.hpp>
class FlushInternal;
class Log
{
public:
static FlushInternal* endl;
Log(int log_length)
{
i = 0;
messages_buffer = new boost::circular_buffer<std::string>(log_length);
}
template <class T>
Log &operator<<(const T &v)
{
current_message << v;
return *this;
}
Log &operator<<(std::ostream&(*f)(std::ostream&))
{
current_message << *f;
return *this;
}
Log &operator<<(FlushInternal*)
{
++i;
messages_buffer->push_back(current_message.str());
clean_stringstream(current_message);
is_filled();
return *this;
}
boost::circular_buffer<std::string> *messages_buffer;
private:
int i;
std::stringstream current_message;
void is_filled()
{
if (i >= messages_buffer->capacity())
{
i = 0;
BOOST_FOREACH(std::string s, *messages_buffer)
{
std::cout << ++i << ": " << s << " ;" << std::endl;
}
i = 0;
}
}
void clean_stringstream(std::stringstream &message)
{
message.flush();
message.clear();
message.seekp(0);
message.str("");
}
};
FlushInternal* Log::endl = 0;
And I can Use it like this:
#include <log.h>
int main()
{
Log l(2);
l << "message one: " << 1 << Log::endl;
l << "message two:" << " " << 2 << Log::endl;
l << "message " << "three: " << 3 << Log::endl;
l << "message" << " " << "four: " << 4 << Log::endl;
std::cin.get();
}
This would output:
1: message one: 1 ;
2: message two: 2 ;
1: message three: 3 ;
2: message four: 4 ;
As you can see I can have as many <<
as I want inside each log message. I want to be capable to use one instance of Log
class from many threads at the same time. So I would have something like (pseudocode that compiles, runs but traces nothing.):
#include <boost/thread.hpp>
#include <log.h>
Log *l;
void fun_one()
{
*l << "message one: " << 1 << Log::endl;
*l << "message two:" << " " << 2 << Log::endl;
}
void fun_two()
{
*l << "message " << "three: " << 3 << Log::endl;
*l << "message" << " " << "four: " << 4 << Log::endl;
}
int main()
{
l = new Log(2);
boost::thread(fun_one);
boost::thread(fun_two);
std::cin.get();
}
So as you can see I want messages to be inserted into log in multythreaded function. Lo I wonder - how to make my log cclass support this?
The approach linked by trojanfoe is pretty much the canonical one. Basically create some temporary thing for the leftmost << operator, accumulate everything, and output the message in the destructor for the temporary thing.
The only question is the exact mechanics of this accumulator. The example used ostringstream
, but I've seen the ofstream
for the log file used directly as well (requires locking to ensure the output ends up on one line).
Creating ostringstreams
is relatively expensive on some platforms, because they may need to lock and copy some internal locale related things. You could re-implement also the << operator for interesting types, but I'd test the ostringstream approach first.
A useful optimization is determine at the point of the construction of the temporary whether the trace will be emitted (e.g., whether tracing is enabled at that particular level), and not create the guts of the temporary at all in that case - all the insertion operations will be no-ops.