0

Let's imagine my program needs input from the user at different times. I want this input to prevent flushing the cout buffer. Can I set cin and cout on different stream buffers?

Example in question: a program that reads two numbers in a line, n1 n2, and depending on the first number being either 0, 1, or 2:

  • n1 = 0: writes the second number n2 to a vector v
  • n1 = 1: outputs v[n2] in cout
  • n1 = 2: pop_back() on v

The MWE is:

#include <iostream>
#include <vector>

using namespace std;

int main() {
    int size, n1, n2;
    vector<int> v;
    cin >> size;

    while(size--){
        cin >> n1;

        if (n1 == 0)
        {
            cin >> n2;
            v.push_back(n2);
        }
        else if (n1 == 1)
        {
            cin >> n2;
            cout << v[n2] << '\n';
        }   
        else if (n1 == 2)
            v.pop_back();
    }

return 0;
}

Say I have this test input

8
0 1
0 2
0 3
2
0 4
1 0
1 1
1 2

correct output should be

1
2
4

The program above yields outputs interspersed with the input lines instead.

But I would like them all printed together at end program, without using different means e.g. storing them in some container etc.

So I think I should operate on the buffer, but how?

Giogre
  • 1,444
  • 7
  • 19
  • 1
    `std::endl` always flushes the stream that it’s inserted into. That’s its job. – Pete Becker Aug 23 '20 at 21:05
  • By default, `cin` and `cout` are `tie()`'d together, so that reading input will first flush any pending output. You can un-`tie()` them by calling `cin.tie(nullptr)`. – Remy Lebeau Aug 24 '20 at 00:55
  • @RemyLebeau: What would the side effects of the untying be if any? – Giogre Aug 24 '20 at 00:59
  • 1
    @Lingo have a look at https://stackoverflow.com/questions/31162367/ – Remy Lebeau Aug 24 '20 at 01:27
  • @RemyLebeau: so according to the link using `cin.tie(NULL)` would do exactly what I asked to, at the price of having to use `cout.flush()` manually when I need the output printed (and I am not using `std::endl`), which here is a small price to pay. That's exactly the behaviour I was looking for. – Giogre Aug 24 '20 at 08:18
  • 1
    @Lingo -- no, un-tie-ing doesn't do everything you're looking for. It eliminates the **forced** flush that you need for interactive input. It does not affect the stream's internal flushing. As an extreme example, if the program wrote enough output to fill the output buffer, the next insertion into the stream would flush the buffer. And you don't want to disable that. – Pete Becker Aug 24 '20 at 13:54
  • @PeteBecker: there is no risk of overfilling the buffer in my case, as my program output is very small, and the only issue it encounters is the forced flushing of output due to its coexistence with input. The `cin.tie(NULL)` trick effectively makes the program behave as I wanted it to. – Giogre Aug 24 '20 at 14:00
  • @Lingo -- as I said, filling the buffer is "an extreme example". There are other reasons that the buffer might get flushed. Again: un-tie-ing is not sufficient, although it might seem to work sometimes. – Pete Becker Aug 24 '20 at 14:15
  • @PeteBecker: so you advocate for the `stringstream` solution. – Giogre Aug 24 '20 at 14:18

2 Answers2

6

You could write to your own std::stringstream buffer, and then output that to std::cout when you're ready.

MWE:

#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>

using std::cin;
using std::cout;
using std::istream;
using std::runtime_error;
using std::stringstream;
using std::vector;

static auto get_int(istream& in) -> int {
    int n;
    if (!(in >> n)) {
        throw runtime_error("bad input");
    }
    return n;
}

int main() {
    auto ss = stringstream();
    auto v = vector<int>();
    auto size = get_int(cin);

    while(size--) {
        auto n1 = get_int(cin);

        if (n1 == 0) {
            auto n2 = get_int(cin);
            v.push_back(n2);
        } else if (n1 == 1) {
            auto n2 = get_int(cin);
            ss << v[n2] << '\n';
        } else if (n1 == 2) {
            v.pop_back();
        }
    }

    cout << ss.str();
}
Eljay
  • 4,648
  • 3
  • 16
  • 27
  • Interesting, `` is quite powerful. Why the persistent use of `auto` when you could just write `int`? I am curious, since these are not some overly complicated custom types. – Giogre Aug 23 '20 at 22:05
  • I prefer to use `auto`, in part because it necessitates initialization especially for fundamental types. – Eljay Aug 23 '20 at 22:08
  • Maybe it is safer, but it obfuscates the code a little bit. – Giogre Aug 23 '20 at 22:15
  • 1
    I think the "obfuscates" is moreso unfamiliarity. Once I got used to it (which was in C# using `var`), I find the code to have more clarity. – Eljay Aug 23 '20 at 22:27
0

No need to modify the buffer. Instead of cout << v[n2] you can store v[n2] in a second vector and print it out on the outside of the loop.

David G
  • 94,763
  • 41
  • 167
  • 253
  • thanks for the prompt reply. What if I cannot/do not want to use containers, as I have stated in the question body? Is there a way to prevent `cin` from flushing out the buffer? – Giogre Aug 23 '20 at 20:16
  • You could write to your own `std::stringstream` buffer, and then output that to `std::cout` when you're ready. – Eljay Aug 23 '20 at 21:15
  • @Eljay: I guess, care to elaborate further with a MWE? – Giogre Aug 23 '20 at 21:31