-1

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

SergeyA
  • 61,605
  • 5
  • 78
  • 137
JoseOrtiz3
  • 1,785
  • 17
  • 28
  • The standard streams are required to be thread-safe. You'll have to be more specific about what you want to accomplish with this lock. – Pete Becker Apr 12 '16 at 21:50
  • @PeteBecker, may be output from a single << operation should not be interleaved? This is a valid requirement. – SergeyA Apr 12 '16 at 22:06
  • @SergeyA - yes, it's certainly possible to guess at what the question is intended to ask. I was looking for something more definitive, which neither you nor I can provide. – Pete Becker Apr 12 '16 at 22:11
  • I did not know streams are required to be thread-safe. There were some errors I was trying to rule out, but they don't seem to be related to threading, but I was having a hard time proving that without this class. But also the interleaving problem where multiple lines are written at the same time should be avoided. The question was simply how to lock-out a ofstream that was being used by multiple threads so only one has access to it at a time. – JoseOrtiz3 Apr 12 '16 at 22:36

3 Answers3

0

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.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • That appears to work fine although for some reason it cannot accept std::endl? – JoseOrtiz3 Apr 12 '16 at 21:35
  • I can `<<` any const strings, including those with a `\n` character, but if I put `stream << std::endl` the program aborts. It also aborts if I try `stream.flush()`. Taking some time trying to figure out why. – JoseOrtiz3 Apr 12 '16 at 22:42
  • It confuses me a lot, but any "flushy" action of the ofstream (< – JoseOrtiz3 Apr 12 '16 at 22:53
  • I think a custom operator<< must be set up for std::endl http://stackoverflow.com/questions/1134388/stdendl-is-of-unknown-type-when-overloading-operator – JoseOrtiz3 Apr 13 '16 at 18:26
0

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;
    }
};
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
0

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;
}
Community
  • 1
  • 1
JoseOrtiz3
  • 1,785
  • 17
  • 28