7

Imagine you have two threads. The first thread tries to print integer as decimal using std::dec:

std::cout << std::dec << 123 << std::endl;

The second thread tries to print integer as hexadecimal using std::hex:

std::cout << std::hex << 0x321 << std::endl;

Is it guaranteed that 123 will be printed as decimal and 0x321 will be printed as hexadecimal? If it is not, how do I do proper std::cout formatting in multithread environment?

C++20 has std::osyncstream. But what can we use before C++20?

anton_rh
  • 8,226
  • 7
  • 45
  • 73
  • [`std::mutex`](https://en.cppreference.com/w/cpp/thread/mutex) – bolov Aug 12 '20 at 12:46
  • 3
    Does this answer your question? [How to easily make std::cout thread-safe?](https://stackoverflow.com/questions/14718124/how-to-easily-make-stdcout-thread-safe) – 0x5453 Aug 12 '20 at 12:46
  • @bolov, what if use third party library that has its own threads? – anton_rh Aug 12 '20 at 12:47
  • @anton_rh I don't understand what you mean. – bolov Aug 12 '20 at 13:06
  • @bolov, I can't force the developers of 3rd party lib to use my mutex. – anton_rh Aug 12 '20 at 13:07
  • are you creating a library that will be used by other libraries? – bolov Aug 12 '20 at 13:08
  • Within the context of this specific question (formatting the numbers), using a `stringstream` to create the text first, and then printing the text should provide the guarantee that you ask for (123 is printed in decimal, 0x321 is printed in hex). Although unlikely, the text of the two prints might still get interleaved. But, the iomanipulators did not get applied to the wrong operands. To solve the general problem, you will probably have to change the `basic_streambuf` to do something atomic for you. – jxh Aug 20 '20 at 06:00
  • @jxh, yes, `stringstream` is a solution. But it involves memory allocating, which can be slow, especially if you run you code on embedded platform with simplified allocator. – anton_rh Aug 20 '20 at 06:42
  • @jxh, why did you delete your answer? Sorry, I didn't have a chance to read it carefully, because I was quite busy recently. – anton_rh Aug 20 '20 at 06:55
  • 1
    There is no solution without either a mutex wrapping the whole access to `std::cout`, or a temporary buffer and then only protecting the final write by a mutex. If you are worried about temporary buffer performance in embedded, then provide a [custom allocator](https://en.cppreference.com/w/cpp/named_req/Allocator) to `std::stringstream` which pre-allocates up to `n` bytes inline (in embedded buffer), and only exceeding allocations in heap instead. – Ext3h Aug 21 '20 at 08:27
  • @Ext3h, yes. I'm not against mutex. But It won't work if 3rd party code output to `std::cout` without my mutex. So locking by internal mechanisms of __ would be preferred. But it seems like that until C++20 it cannot be done. – anton_rh Aug 21 '20 at 11:36
  • 1
    @anton_rh: If I am reading it correctly, `osyncstream` itself does not change `cout` to have mutual exclusion properties. It is a type that can act like a synchronous stream. So it doesn't solve the 3rd party issue you are raising. – jxh Aug 22 '20 at 05:04

2 Answers2

3

From the get-go, this isn't an option with std::cout.

If you just want to use a different object a simpel way is just use stringstream for each 'compound' needed eg.:

std::cout << (std::ostringstream{} << std::hex << 0x321 << std::endl).str();

Alternatively you can make your own stream class that just forwards everything to std::cout on destruction (eg. you could have std::ostringstream as a member or inherit it):

~MyStringStream(){
  std::cout << str();
}

I wouldn't recommend changing this fact on the actual std::cout because others will not expect std::cout to behave in this or that different way. However with that being said I think it is possible with redirection, so I created a way to showcase this (somewhat of a hack: I consider everything that does something like this a hack) and how to make this possible. Please note this isn't a finished solution at all, it just shows how to get std::cout to go through your own stream class, which then needs to be implemented/overridden 'correctly', made thread-safe and then added the neccesary synchronizations, or however you plan to get that extra level, etc. Please also note I haven't considered how this interferes with the std::cout tie'ed streams (eg. std::in, std::err), but I guess it's not a big deal.

Try it yourself on godbolt

#include <utility>
#include <string>
#include <iostream>
#include <sstream>


std::stringstream new_out;

class SyncedStreamBuf : public std::stringbuf {
public:
  SyncedStreamBuf(){}
  

  virtual int sync() override {
    new_out << "From override: " << str();
    str("");//empty buffer
    return 0;//success
  }
};

class SyncedStream : public std::ostream {
public:
  SyncedStream() : std::ostream(&syncedStreamBuf_){
  }

private:
  SyncedStreamBuf syncedStreamBuf_;
};

SyncedStream my_stream;

int main()
{
    std::streambuf* cout_buff = std::cout.rdbuf(); // save pointer to std::cout buffer
 
    std::cout.rdbuf(my_stream.rdbuf());//redirect cout to our own 'stuff'
    
    static_cast<std::ostream&>(new_out).rdbuf(cout_buff);//put cout's buffer into a new out stream
    
    
    new_out << "test: new_out now prints to stdout\n";
    std::cout << "some message\n";//<--now goes through our overridden class
    std::cout.flush();

    std::cout << "you will see this message - didn't flush\n";
}

Output:

test: new_out now prints to stdout

From override: some message
darune
  • 10,480
  • 2
  • 24
  • 62
  • 1
    Buffering in a separate buffer alone isn't going to suffice. A statically scoped mutex around the access to `std::cout` is still required in order to prevent interleaving on a character-by-character base. Furthermore, your first example didn't flush due to the intermediate cast to a string, loosing the usual side effects of `std::endl`. – Ext3h Aug 21 '20 at 08:20
  • @Ext3h Great comment, thank you. Im not sure if you saying it's possible or impossible with this method ? I still think it should be possible with redirection to own buffer. – darune Aug 24 '20 at 07:50
0

Use a function to print output. A output will only be printed from this function. Use mutex inside this function.

OutputFunction(int form, const int& value)
{
    std::lock_guard<std::mutex> m_OutputMutex;
    if(form == 0)// 0 means dec
    {
        std::cout << std::dec << value << std::endl;
    }
    elseif(form ==1)
    {
        std::cout << std::hex << value << std::endl;
    }
} 
lalit gangwar
  • 389
  • 4
  • 10