I am trying to write a thread safe logger class so that i can do the exact same as with cout but with thread safety.
here is the logger class (still working on the type of lock required)
class logger {
public:
logger(LOGGER::output_type type);
logger(const logger& orig);
virtual ~logger();
template <typename T>
logger & operator << (const T & data){
boost::mutex::scoped_lock io_mutex_lock(io_mutex);
(*out)<<data;
return *this;
}
private:
static boost::mutex io_mutex;
std::ostream * out;
};
The poblem is I cannot do the following
log<<"asdfg";
I have to instead do
log<<string("asdfg");
int i = 10;
log<<string ("i = ") << i << endl;
following is the compilation error.
gcc.compile.c++ src/simpleThread/bin/gcc-4.4.5/debug/simpleThread.o
src/simpleThread/simpleThread.cc: In function ‘int main()’:
src/simpleThread/simpleThread.cc:28: error: no match for ‘operator<<’ in ‘((logger*)logOut.logger::operator<< [with T = char [18]](((const char (&)[18])"fibonacci thread ")))->logger::operator<< [with T = int](((const int&)((const int*)(& i)))) << std::endl’
So I guess i am missing some important concept of C++. Please let me know what it is?
Is my requirement even achievable
thanks,
Kiran
Note that your logger class is still not thread safe:
int i = 10;
log <<string ("i = ") << i << endl;
There is nothing stopping this thread from getting preempted by another another thread printing to logger and producing something like:
i = i = 12
Instead of:
i = 1
i = 2
If you have a compiler with variadic templates, here's one way of fixing this:
#include <ostream>
#include <mutex>
inline void sub_print(std::ostream&) {}
template <class A0, class ...Args>
void
sub_print(std::ostream& os, const A0& a0, const Args& ...args)
{
os << a0;
sub_print(os, args...);
}
std::mutex&
io_mut()
{
static std::mutex m;
return m;
}
template <class ...Args>
void
log(std::ostream& os, const Args& ...args)
{
std::lock_guard<std::mutex> _(io_mut());
sub_print(os, args...);
os.flush();
}
#include <iostream>
int main()
{
int i = 10;
log(std::cout, "i = ", i, '\n');
}
I.e. the mutex is locked until all arguments for given log message are processed. std::endl is handled separately by always flushing the stream after every message.
Your problem is that logger isn't a stream, so the usual stream operators will not work, just the single one you have defined.
You can get endl to work by defining it yourself:
inline logger& endl(logger& log)
{
// do something to *out
return log;
}
Now log << "Hello!" << endl; will work.
To be able to chain several << operations together like the streams do, you will have to define all the operators for the logger (just like the streams do).
I think you are introducing synchronization at too low a level. To understand why, assume thread 1 executes:
log << "This is " << "my " << "log message" << endl;
while thread 2 executes:
log << "Hello, " << "World!" << endl;
In such a case, the log file (or console output) may contain interleaved messages, for example:
This is Hello, my World!
log message
To avoid this problem, your application will have to construct
an entire message as a single string, and only then pass that string to a logger object. For example:
ostringstream msg;
msg << "This is " << "my " << "log message" << endl;
log << msg.str();
If you take this approach, then your logger class does not need to overload operator<< for endl and multiple types.
I tested your program with some simplifications as follows, and it compiles and runs fine, which means that the problem is probably elsewhere:
#include <iostream>
class logger {
public:
// logger(LOGGER::output_type type);
logger(std::ostream& os): out(&os) {}
~logger() {}
template <typename T>
logger & operator << (const T & data){
// boost::mutex::scoped_lock io_mutex_lock(io_mutex);
(*out)<<data;
return *this;
}
private:
// static boost::mutex io_mutex;
std::ostream * out;
};
int main()
{
logger log(std::cout);
log << std::string("hello ");
log << "world\n";
}
After digging into iostreams and with hints from Bo Persson, I think i found a better solutions since I dont need to write a function each for each ios manipulator. So here it is
logger& operator<<(std::ostream& (*pf)(std::ostream&)) {
(*out)<<pf;
return *this;
}
For an explanation search for iostreams and applicators.
Here is the complete boost::thread safe implementation (requires some refactoring and optimization probably) using some hints from Ciaran-Mchale
/*
* File: logger.h
* Author: Kiran Mohan
*
*/
#ifndef LOGGER_H
#define LOGGER_H
#include <boost/thread.hpp>
#include <iostream>
namespace LOG {
enum output_type {
STDOUT,
STDERR
};
/**
* a thread safe logger to print to stdout or stderr
*/
class logger {
public:
logger(LOG::output_type type);
logger(const logger& orig);
virtual ~logger();
template <typename T>
logger & operator <<(T data) {
/* Takes any data type and stores in a stringstream
*/
(*oss) << data;
return *this;
}
logger & operator<<(std::ostream& (*pf)(std::ostream&)) {
// for stream manipulators
(*oss) << pf;
return *this;
}
logger & operator<<(logger & (*pf)(logger &)) {
//applicator - mainly calling the print function;
return pf(*this);
}
friend logger & flush(logger & l);
logger & print() {
boost::mutex::scoped_lock io_mutex_lock(io_mutex);
(*out) << oss->str() << std::flush;
delete oss;
oss = new std::ostringstream;
return *this;
}
private:
static boost::mutex io_mutex;
std::ostream * out;
std::ostringstream * oss;
};
logger & flush(logger & l);
};
#endif /* LOGGER_H */
/*
* File: logger.cc
* Author: aryan
*
*/
#include <boost/thread/pthread/mutex.hpp>
#include <iostream>
#include "logger.h"
using namespace LOG;
boost::mutex logger::io_mutex;
logger::logger(LOG::output_type type) {
if (type == LOG::STDOUT) {
out = &std::cout;
} else {
out = &std::cerr;
}
oss = new std::ostringstream;
}
logger::logger(const logger& orig) {
out = orig.out;
}
logger::~logger() {
delete oss;
}
logger & LOG::flush(logger & l) {
l.print();
boost::this_thread::yield();
return l;
}
use it like this
LOG::logger logOut (LOG::STDOUT);
logOut<<"Hello World\n"<<LOG::flush;
Related
I'm currently writing a class to wrap stringstream. My overall goal is to provide a threadsafe << (ostream) for my stringstream. I'm having trouble figuring out the correct syntax of what I'm trying to do. Any help would be most appreciative!
Below you'll find what I've tried. I know this is not the correct syntax, but it is kind of what I'm shooting for. I could of course not overload the << operator and force users to use the AddValue, but that wouldn't be ideal for quick and easy string manipulation while writing the code.
class WrappedStringStream
{
public :
WrappedStringStream() ;
template<typename T>
void AddValue( const T & value )
{
m_mutex.Lock() ;
//here I'd like to do something like m_stringstream << value, but of course since
//I'm overloading operator<< that won't work
m_mutex.Unlock() ;
}
friend std::ostream & operator<<( std::ostream & out, const WrappedStringStream & string )
{
string.AddValue( out ) ;
return out ;
}
protected :
std::stringstream m_stringstream ;
mutable Mutex m_mutex ;
}
As mentioned above it doesn't compile, which I understand since I'm passing WrappedStringStream as a const parameter and calling AddValue which isn't const - resulting in the discards qualifier error.
Here is the solution
#include <iostream>
#include <sstream>
#include <mutex>
using namespace std;
class MutexWrapper
{
private:
mutex& m_mutex;
public:
MutexWrapper(mutex& mtx) : m_mutex(mtx) { mtx.lock () ; };
~MutexWrapper() { m_mutex.unlock () ; };
};
class WrappedStringStream
{
public :
WrappedStringStream() { };
template<typename T>
std::ostream & operator<<(const T& value)
{
MutexWrapper wrapper(m_mutex);
return m_stringstream << value;
}
void showStream()
{
cout << m_stringstream.str();
}
protected :
stringstream m_stringstream;
mutable mutex m_mutex ;
};
int main()
{
WrappedStringStream ws;
ws << "This is a string, " << 5 << 6.78;
ws.showStream();
return 0;
}
Output
This is a string, 56.78
======== edit ==========
Originally I was not quiet understand what the questioner's final goal, but just focus on how to fix his syntax issue.It is not a good idea to use << in a multithreading environment. We have a Log class, and in our log class we only have a Log method which takes variable amount of parameters. That will solve the problem.
Still, there is a solution for using << to lock the thread, but really urgly and NOT recommend. Downside is obvious - if you forgot to add the 'LoggingStart' and 'LoggingEnd', you might caught deadlock.
Also thanks #RemyLebeau, It should return *this instead of m_stringstream.
Please see code below.
#include <iostream>
#include <sstream>
#include <mutex>
using namespace std;
class WrappedStringStream
{
public:
enum StreamSignals
{
LoggingStart,
LoggingEnd
};
WrappedStringStream() { };
std::ostream & operator<<(const StreamSignals& signal)
{
if (signal == LoggingStart)
m_mutex.lock();
else if (signal == LoggingEnd)
m_mutex.unlock();
return *this;
}
template<typename T>
std::ostream & operator<<(const T& value)
{
m_stringstream << value;
return *this;
}
void showStream()
{
cout << m_stringstream.str();
}
protected :
stringstream m_stringstream;
mutable mutex m_mutex ;
};
int main()
{
WrappedStringStream ws;
ws << WrappedStringStream::StreamSignals::LoggingStart;
ws << "This is a string, " << 5 << 6.78;
ws << WrappedStringStream::StreamSignals::LoggingEnd;
ws.showStream();
return 0;
}
I implemented a Logger, so it can be used like ostream.
e.g, if someone wants to write to the log - he can do something like this:
LOG << "hello world " << 6 << 8.6 << "\n";
The log will be written to the screen, to log file, and to any other source the user like to (this isn't the issue).
To achieve this goal, I created a macro for LOG:
#define LOG Logger::GetInstance()(__FILENAME__, __func__, __LINE__)
and overloaded operator () and operator <<:
template <typename T>
inline Logger& operator << (const T& msg) {
std::stringstream ss;
ss << msg;
PrintToFile(ss.str());
PrintToScreen(ss.str());
return *this;
}
Logger& Logger::operator () (const std::string& sourceFile, const std::string& funcName, int lineNumber) {
std::stringstream ss;
ss << Utilities::GetFormattedTime("%d.%m.%y %H:%M:%S") << "::" << sourceFile << "(" << lineNumber << ")::" <<
funcName << "::";
PrintToFile(level, ss.str());
PrintToScreen(level, ss.str());
return *this;
}
The problem is when I have more than one thread that is running over my process, the print may be cut, because of context switch between the threads in the middle of the example line above (LOG << "hello world... ")
Using a mutex may not help, because operator << and operator () are not the same function.
Is there any brilliant or simple solution for this problem?
Just from top of my head. If you want to keep your approach with stream io operators as is, you can use sort of proxy object that locks\unlocks mutex.
Please, don't draw attention to coding style (especially swfull and dangerous Logger implementation). Below you can find brief illustration of mentioned idea.
template<class TLogger, class TLockObject>
class LoggerProxy
{
public:
LoggerProxy(TLogger &logger, TLockObject &lockObj)
: m_logger(logger),
m_lockGuard(lockObj)
{
}
template <typename T>
inline LoggerProxy& operator << (const T& msg)
{
m_logger.Write(msg);
return *this;
}
private:
TLogger & m_logger;
std::lock_guard<typename TLockObject> m_lockGuard;
};
//Purpose of code below is just an illustration of usage LoggerProxy class. Don't use it in production code.
class Logger
{
public:
static Logger& GetInstance()
{
static Logger instance;
return instance;
}
static std::mutex& GetLockObject()
{
static std::mutex lockObj;
return lockObj;
}
template <typename T>
inline void Write (const T& msg) {
std::cout << msg << std::endl;
}
};
#define LOG LoggerProxy<Logger, std::mutex>(Logger::GetInstance(), Logger::GetLockObject())
int main()
{
LOG << 10 << "HELLO" << 1.1;
LOG << 101 << "HELLO2" << 11.1;
return 0;
}
You are right that operator<< and operator () are two different function, but that doesn't mean you cannot use a mutex.
From what i can gather from your example in both operator<< and operator() you call at the end the same two functions PrintToFile and PrintToScreen.
This makes me think that the stream common to all threads is in these two functions and not in your operator<< and operator(), so you can actually lock a mutex inside those functions and have thread safe access to the logger.
That being said it remains only to decide if you need two mutex or only one, that depends if you want the logging to be "atomic" as operation or you want to split it.
In the "atomic" version you should use a single version that holds a single mutex while writting to both screen and file thus your logs will execute sequentually.
Instead if you the splitted version you want to have two different functions with two different mutexes, one for the file logging and one for the screen loggin, this one a thread that wants to write to the file doesn't have to wait for threads that have already written to the file and are now writting to the screen. As always remember that having two mutexes has cost.
Share less, lock less.
If you're gonna synchronize on each operator<< you're going to have very lousy performance when your application is compiled with logging enabled.
This is a sketch of how I'd do it:
The Concept
namespace Logging {
struct SinkConcept { void commit(std::string const&); }; // documentation only
Let's design a narrow contract where any logging sink has just that one method.
Now, let's create a LogTx - representing a log-transaction.
LogTx should be a move-aware temporary object creates a log message locally. This means that the buffers are not shared and need not be synchronized until you commit.
Commit is done from the destructor:
// movable log transaction (using arbitrary sink)
template <typename Sink> struct LogTx {
LogTx(Sink& s) : _sink(s) {}
LogTx(LogTx&&) = default;
unique_flag _armed;
std::ostringstream _oss;
Sink& _sink;
~LogTx() { if (_armed) _sink.commit(_oss.str()); }
template <typename T> LogTx& operator<<(T&& rhs)& { return (_oss << rhs), *this; }
template <typename T> LogTx operator<<(T&& rhs)&& { return (_oss << rhs), std::move(*this); }
};
That's all. The _armed flag makes sure the destructor doesn't commit in moved-from instances.
Some Sample Sinks
Now, let's add simple sinks so we can make a demo. Let's start with the simplest:
struct NullSink { void commit(std::string const&) const {} };
Now, let's get more useful. A sink to commit log transactions to any ostream object or reference:
template <typename Impl, bool Flush = true>
struct StreamSink {
StreamSink(Impl stream_or_ref = {}) : _sor(std::move(stream_or_ref)) {}
StreamSink(StreamSink&& rhs) : StreamSink(std::move(rhs._sor)) {}
void commit(std::string const& msg) {
std::lock_guard<std::mutex> lk(_mx);
get() << msg << "\n";
if (Flush) get() << std::flush;
}
std::ostream& get() { return _sor; }
private:
mutable std::mutex _mx;
Impl _sor; // stream convertible to ostream&
};
And, because you were writing to several destinations in your example:
template <typename A, typename B> struct TeeSink { // dispatch to two sinks
A a; B b;
void commit(std::string const& msg) { a.commit(msg); b.commit(msg); }
};
Some Convenience Functions
Unless you use C++17, some factory functions will be welcome.
// factory functions (mostly not needed in c++17 with deduction guides)
template <typename A, typename B>
TeeSink<A, B> tee(A&& a, B&& b) { return { std::forward<A>(a), std::forward<B>(b) }; }
StreamSink<std::ofstream, false> log_to(std::ofstream&& file) { return {std::move(file)}; }
StreamSink<std::reference_wrapper<std::ostream>, true> log_to(std::ostream& os) { return {os}; }
Let's also add global instances of sinks for standard streams, so that you can use these to get the same synchronization everywhere:
auto& log_to_stderr() {
static StreamSink<std::reference_wrapper<std::ostream>, true> s_instance { log_to(std::cerr) };
return s_instance;
}
auto& log_to_stdout() {
static StreamSink<std::reference_wrapper<std::ostream>, true> s_instance { log_to(std::cout) };
return s_instance;
}
auto& null_sink() {
static NullSink const s_instance{};
return s_instance;
}
template <typename Sink>
LogTx<Sink> make_tx(Sink& sink) { return {sink}; }
Finally the pièce de resistance: makeTx to create a LogTx for a given sink:
template <typename Sink>
LogTx<Sink> make_tx(Sink& sink) { return {sink}; }
DEMO TIME
Now we can put it together:
#define LOG_TO(sink) (Logging::make_tx(sink) << __FILE__ << ":" << __LINE__ << "\t" << __func__ << "\t")
#ifdef NOLOGGING
#define LOG LOG_TO(Logging::null_sink())
#else
static auto _file_sink = Logging::log_to(std::ofstream("demo.log"));
static auto _both_sink = tee(_file_sink, Logging::log_to_stderr());
#define LOG LOG_TO(_both_sink)
#endif
This is pretty much what you wanted:
Live On Coliru
#include <thread>
void worker(std::string id) {
while (auto r = rand()%10) {
std::this_thread::sleep_for(std::chrono::milliseconds(r));
LOG << "Ping from " << id;
}
}
int main() {
LOG << "Hello";
{
std::thread a(worker, "A"), b(worker, "B");
a.join();
b.join();
}
LOG << "Bye";
}
Prints, to both stderr and demo.log:
main.cpp:104 main Hello
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from B
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from B
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from B
main.cpp:99 worker Ping from B
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from B
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from A
main.cpp:99 worker Ping from A
main.cpp:110 main Bye
C++11 Full Listing
Added a c++11-compatible version, of which I include the full listing as to protect against link rot:
[C++11 Live On Coliru][http://coliru.stacked-crooked.com/a/6360aad26b037df2)
#include <functional> // for std::reference_wrapper
#include <iostream>
#include <sstream>
#include <fstream>
#include <mutex>
namespace Logging {
// utility to safely implement movable log transactions
struct unique_flag {
bool value = true;
unique_flag() = default;
unique_flag(unique_flag&& rhs) : value(rhs.value) { rhs.value = false; }
operator bool() const { return value; }
};
struct SinkConcept { void commit(std::string const&); }; // documentation only
// movable log transaction (using arbitrary sink)
template <typename Sink> struct LogTx {
LogTx(Sink& s) : _sink(s) {}
LogTx(LogTx&&) = default;
unique_flag _armed;
std::ostringstream _oss;
Sink& _sink;
~LogTx() { if (_armed) _sink.commit(_oss.str()); }
template <typename T> LogTx& operator<<(T&& rhs)& { return (_oss << rhs), *this; }
template <typename T> LogTx operator<<(T&& rhs)&& { return (_oss << rhs), std::move(*this); }
};
// Some sink models
struct NullSink { void commit(std::string const&) const {} };
template <typename Impl, bool Flush = true>
struct StreamSink {
StreamSink(Impl stream_or_ref = {}) : _sor(std::move(stream_or_ref)) {}
StreamSink(StreamSink&& rhs) : StreamSink(std::move(rhs._sor)) {}
void commit(std::string const& msg) {
std::lock_guard<std::mutex> lk(_mx);
get() << std::move(msg);
if (Flush)
get() << std::endl;
else
get() << "\n";
}
std::ostream& get() { return _sor; }
private:
mutable std::mutex _mx;
Impl _sor; // stream convertible to ostream&
};
template <typename A, typename B> struct TeeSink { // dispatch to two sinks
A a; B b;
void commit(std::string const& msg) { a.commit(msg); b.commit(msg); }
};
// factory functions (mostly not needed in c++17 with deduction guides)
template <typename A, typename B>
TeeSink<A, B> tee(A&& a, B&& b) { return { std::forward<A>(a), std::forward<B>(b) }; }
StreamSink<std::ofstream, false> log_to(std::ofstream&& file) { return {std::move(file)}; }
StreamSink<std::reference_wrapper<std::ostream>, true> log_to(std::ostream& os) { return {os}; }
StreamSink<std::reference_wrapper<std::ostream>, true>& log_to_stderr() {
static StreamSink<std::reference_wrapper<std::ostream>, true> s_instance { log_to(std::cerr) };
return s_instance;
}
StreamSink<std::reference_wrapper<std::ostream>, true>& log_to_stdout() {
static StreamSink<std::reference_wrapper<std::ostream>, true> s_instance { log_to(std::cout) };
return s_instance;
}
NullSink const& null_sink() {
static NullSink const s_instance{};
return s_instance;
}
template <typename Sink>
LogTx<Sink> make_tx(Sink& sink) { return {sink}; }
}
#define LOG_TO(sink) (Logging::make_tx(sink) << __FILE__ << ":" << __LINE__ << "\t" << __func__ << "\t")
#ifdef NOLOGGING
#define LOG LOG_TO(Logging::null_sink())
#else
static auto _file_sink = Logging::log_to(std::ofstream("demo.log"));
static auto _both_sink = tee(_file_sink, Logging::log_to_stderr());
#define LOG LOG_TO(_both_sink)
#endif
#include <thread>
void worker(std::string id) {
while (auto r = rand()%10) {
std::this_thread::sleep_for(std::chrono::milliseconds(r));
LOG << "Ping from " << id;
}
}
int main() {
LOG << "Hello";
{
std::thread a(worker, "A"), b(worker, "B");
a.join();
b.join();
}
LOG << "Bye";
}
The first feeling is, that operator() and operator<< are unrelated. For that it feels better to use two mutex, one in PrintToFile and one in PrintToScreen.
But you also can use the same mutex to log both of your operator methods. As the first operator is a template function, we have not two but n functions. Each template instance is an additional one. So using a single mutex for all the functions did exactly what you want.
If your Logger is a class and the operators are members, you simply make your mutex a member variable which can be (un-)locked in each of your methods.
Use a std::mutex and lock on first use of operator() or operator<<. Then unlock if user string ends with \n. I assume you are sure that user finishes every log entry with linebreak.
class Logger {
std::mutex mux;
//...
public:
Logger& operator()(/*...*/) {
mux.lock();
// write to log
}
Logger& operator<<(const string& str) {
// write to log
if(str[str.size()-1] == '\n')
mux.unlock();
}
//...
};
I'm trying to make my own logging class in C++ using a wrapper class in which i overloaded operator<< that sends it to the cout. Now I want to change it, so that when i create instance of that class, i can pass and argument that logs data in std::cout or some file i create. What is the exact type that is superclass of both fstream and ostream? I tried with std::ios&, std::basic_ios&, std::basic_ostream& and none of them seems to work (throwing me compilation error).
class myostream {
public:
static int getlogLevel() {
return loglevel;
}
static void setlogLevel(int i) {
loglevel = i;
}
myostream(std::basic_ios& cout, int level)
: _cout(cout), _level(level)
{}
template<class T>
std::ostream& operator<<(T t) {
if(_level >= loglevel) {
_cout << loglevelcolor[_level] << loglevelname[_level] << " " << t << COL_RESET << std::endl;
}
return _cout;
}
private:
static int loglevel;
std::basic_ostream& _cout;
int _level;
};
Use base class std::ostream which is typedef for basic_ostream<char>, reference: iostream hierarchy.
Works for me (std::cout, std::ofstream):
#include <iostream>
class myostream {
public:
myostream(std::ostream& out)
: _out(out) {}
template<class T>
std::ostream& operator<<(T t) {
_out << "test" << " " << t << '\n' << 42 << std::endl;
return _out;
}
private:
std::ostream& _out;
};
What is the exact type that is superclass of both fstream and ostream?
It is std::ostream, which is an alias to std::basic_ostream<char>. See the class diagram for std::fstream.
Example:
class myostream {
public:
myostream(int level) // Log into stdout.
: _cout(std::cout), _level(level)
{}
myostream(char const* filename, int level) // Log into a file.
: _file(filename), _cout(_file), _level(level)
{
if(!_file.is_open())
throw std::runtime_error("Failed to open " + std::string(filename));
}
// ...
private:
std::ofstream _file;
std::ostream& _cout;
int _level;
};
I am having the worst time trying to make a class that has a std::ofstream and a std::mutex that is locked and unlocked to control access to the ofstream.
Basically I want a class thread_safe_ofstreamwith a << operator so I can use it like:
thread_safe_ofstream ofs;
ofs << "debug info from different threads" << std::endl;
So I know I need an operator<< overload. While there is plenty of info for this operator<< for classes on the right-hand-side of <<, I cannot find any documentation for how to implement your own ostream-like << input.
I know the following code cannot work because of whatever input/output requirements << has, but this is the spirit of the class I need.
Class thread_safe_ofstream
{
std::mutex mu;
std::ofstream stream;
template<typename T>
operator<<(T& thing)
{
mu.lock();
stream << thing;
mu.unlock();
}
};
This way a single thread_safe_ofstream can be <<'d to from multiple threads without problems (my hope is).
Something like that?
template<typename T>
thread_safe_ofstream& operator<<(const T& thing)
{
std::lock_guard<std::mutex> guard(mu);
stream << thing;
return *this;
}
However, it is usually better to implement operator << as a free function, rather than class member.
You can have such implementation for instance:
class thread_safe_ofstream
{
std::mutex mu;
std::ofstream stream;
template<typename T>
void put(const T& thing) {
std::lock_guard<std::mutex> lock(mu);
stream << thing;
}
friend template<typename T>
thread_safe_ofstream& operator<<(thread_safe_ofstream& tsof, const T& value) {
tsof.put(value);
return tsof;
}
};
Here is a complete class that will print mostly-ungarbled text from commands like ts_ofs << "print this message" << std::endl except between subsequent <<s another thread may start printing. I suspect there could a fix for that but I have no idea how to do it. Inspiration is from this post.
struct ts_ofstream
{
std::ofstream ostream;
std::mutex mu;
//This is only for testing, you can initialize ostream however/wherever you like.
void init(bool test = false)
{
ostream = std::ofstream("C:/Users/Joey/Desktop/THREAD_DEBUG.txt", ios::out);
if (test)
{
ostream << "Testing stream ";
ostream << std::endl;
ostream << "Testing complete" << std::endl;
}
}
template <typename T>
friend ts_ofstream& operator<<(ts_ofstream& s, const T& x);
// function that takes a custom stream, and returns it
typedef ts_ofstream& (*MyStreamManipulator)(ts_ofstream&);
// take in a function with the custom signature
ts_ofstream& operator<<(MyStreamManipulator manip)
{
// call the function, and return it's value
return manip(*this);
}
// define the custom endl for this stream.
// note how it matches the `MyStreamManipulator`
// function signature
static ts_ofstream& endl(ts_ofstream& stream)
{
// print a new line
stream.ostream << std::endl;
return stream;
}
// this is the type of std::ofstream
typedef std::basic_ostream<char, std::char_traits<char> > CoutType;
// this is the function signature of std::endl
typedef CoutType& (*StandardEndLine)(CoutType&);
// define an operator<< to take in std::endl
ts_ofstream& operator<<(StandardEndLine manip)
{
// call the function, but we cannot return it's value
manip(this->ostream);
return *this;
}
};
template<typename T>
ts_ofstream & operator<<(ts_ofstream & s, const T & x)
{
std::lock_guard<std::mutex> lock(s.mu);
s.ostream << x;
return s;
}
I am overloading operator << to implement a stream like interface for a class:
template<typename T>
CAudit& operator << ( const T& data ) {
audittext << data;
return *this;
}
CAudit& operator << ( LPCSTR data ) {
audittext << data;
return *this;
}
The template version fails to compile with "fatal error C1001: INTERNAL COMPILER ERROR (compiler file 'msc1.cpp', line 1794)". Non-template functions all compile correctly.
Is this due to VC6s deficiencies when handling templates and is there a way around this?
Thanks,
Patrick
EDIT :
the full class is:
class CAudit
{
public:
/* TODO_DEBUG : doesn't build!
template<typename T>
CAudit& operator << ( const T& data ) {
audittext << data;
return *this;
}*/
~CAudit() { write(); }//If anything available to audit write it here
CAudit& operator << ( LPCSTR data ) {
audittext << data;
return *this;
}
//overload the << operator to allow function ptrs on rhs, allows "audit << data << CAudit::write;"
CAudit& operator << (CAudit & (*func)(CAudit &))
{
return func(*this);
}
void write() {
}
//write() is a manipulator type func, "audit << data << CAudit::write;" will call this function
static CAudit& write(CAudit& audit) {
audit.write();
return audit;
}
private:
std::stringstream audittext;
};
The problem occurs with the function overload of operator << which is used to allow write() to be used as a stream manipulator:
CAudit audit
audit << "Billy" << write;
That overload of the template for function pointers surely is too much for good old Visual Studio 6.
As a workaround you could define a type for your manipulator and overload operator<< for that type.
Here's some code:
#include "stdafx.h"
#include <string>
#include <iostream>
#include <sstream>
#include <windows.h>
class CAudit {
std::ostringstream audittext;
void do_write() {}
public:
~CAudit() { do_write(); }
// types for manipulators
struct Twrite {};
// manipulators
static Twrite write;
// implementations of <<
template<typename T>
CAudit& operator << ( const T& data ) {
audittext << data;
return *this;
}
CAudit& operator << ( LPCSTR data ) {
audittext << data;
return *this;
}
CAudit& operator << ( Twrite& ) {
do_write();
return *this;
}
};
// static member initialization
CAudit::Twrite CAudit::write;
int main(int argc, char* argv[])
{
CAudit a;
int i = 123;
const char * s = "abc";
a << i << s << CAudit::write;
return 0;
}
The kind of error definitely looks like the kind of crashes caused by VC6's pre-previous-standard implementation of templates.
The best advice is of course to upgrade to either VC7.0, 7.1, 8.0, 9.0 or the beta of 10.
To compare that to Windows versions, it's still using Windows 98 when Me, 2000, XP, Vista and 7 are available.
Having said that, you can simplify the lookup a lot by a simple trick:
class CAudit {
template<typename T>
CAudit& operator<<(T const& t) {
this->print(t);
return *this;
}
private:
void print(int);
void print(LPCSTR);
void print(CAudit & (*func)(CAudit &));
template<typename T> print(T const&);
};
The hope here is that the first lookup of operator<< finds the single member template. The other operator<< candidates are non-members for other classes and built-ins. They should be unambiguously worse that this template. The second lookup inside your operator only needs to deal with CAudit members called print.
template<typename T>
CAudit& operator << (T data ) {
audittext << data;
return *this;
}
EDIT:
#include <iostream>
using namespace std;
class CAudit{
public:
CAudit(){}
template< typename T >
CAudit &operator<<(T arg);
CAudit &operator<<(char s);
};
template< typename T>
void oldLog(T arg){
cout << arg;
}
template< typename T >
CAudit &CAudit::operator<<(T arg){
oldLog( arg );
return *this;
}
CAudit &CAudit::operator<<(char arg){
oldLog( arg );
return *this;
}
int main(){
CAudit e;
e << "Hello";
e << 'T';
return 0;
}
The problem does not seem to be in the code snippet you posted. This program works fine:
#include "stdafx.h"
#include <string>
#include <iostream>
#include <sstream>
#include <windows.h>
class CAudit {
std::ostringstream audittext;
public:
std::string getAuditText() const { return audittext.str(); }
template<typename T>
CAudit& operator << ( const T& data ) {
audittext << data;
return *this;
}
CAudit& operator << ( int data ) {
audittext << data;
return *this;
}
CAudit& operator << ( LPCSTR data ) {
audittext << data;
return *this;
}
};
int main(int argc, char* argv[])
{
CAudit a;
int i = 123;
const char * s = "abc";
a << i;
a << s;
std::cout << "audittext is: '" << a.getAuditText() << "'\n";
return 0;
}
Could you post some more code?