Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

thread safe streams and stream manipulators

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

  1. log<<"asdfg";
    I have to instead do
    log<<string("asdfg");

  2. 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

like image 363
Kiran Mohan Avatar asked Apr 16 '26 05:04

Kiran Mohan


1 Answers

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.

like image 158
Howard Hinnant Avatar answered Apr 18 '26 22:04

Howard Hinnant



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!