1

I have some C++ console programs that display progress information on the last line of output, at regular intervals.

This progress line is cleared prior to writing the next real output (or updated progress information); this could be from a number of different places in the source, and I'm currently clearing the progress line on each one, e.g.:

cout << clearline << "Some real output" << endl;
...
cout << clearline << "Some other real output" << endl;
...
cout << clearline << setw(4) << ++icount << ") " << ... << endl;
...
cout << clearline << "Progress info number " << ++iprog << flush;

Here, 'clearline' is some (system dependent) string like "\r\33[2K" which clears the current last line.

I would prefer something cleaner, that localises source changes to the actual line that's going to be cleared, like simply:

cout << "Progress info number " << ++iprog << flush << defer_clearline;

where 'defer_clearline' causes the writing of 'clearline' to be deferred until just prior to the next cout output, wherever and whatever that happens to be. I then wouldn't need to use 'clearline' on all the other lines.

I thought it might be possible to do this if 'defer_clearline' is a manipulator, and/or using xalloc() and iword(). But I've not managed to get anything that works. Is it possible to do this sort of thing, and if so how?

2020-12-30: edited to include missing 'flush's.

  • Personally I would write an `overwrite_cout` object that wraps `cout` and use `clearline` as the first thing passed to it so that every time you use it, it automatically cleans the previous line. – NathanOliver Dec 30 '20 at 14:57
  • Might be possible to implement a custom [`std::streambuf`](https://en.cppreference.com/w/cpp/io/basic_streambuf) that provides the desired behaviour. It could keep track of the number of characters written on the line, map '\n' to '\r' etc. Fairly basic but... ? – G.M. Dec 30 '20 at 15:07
  • Related/dupe: [https://stackoverflow.com/questions/799599](https://stackoverflow.com/questions/799599/c-custom-stream-manipulator-that-changes-next-item-on-stream) – IlCapitano Dec 30 '20 at 17:20

1 Answers1

0

You can pretty easily setup an std::cout wrapper:

// Declare the empty struct clear_line and instantiate the object cls
struct clear_line { } cls;

class out {
private:
    std::ostream &strm = std::cout;
    bool is_next_clear = false;
public:
    template <typename T>
    out& operator<<(const T& obj) {
        if(is_next_clear) {
            strm << std::endl << std::endl << std::endl; // clear logic
            is_next_clear = false;
        }
        
        strm << obj;
        return *this;
    }
    
    out& operator<<(const clear_line& _) {
        is_next_clear = true;
        return *this;
    }
};

This pretty simply stores an is_next_clear bool for whether or not the next regular output should be cleared. Then, in the general case (the templated operator<<()), we run your clear logic and flip the is_next_clear flag if applicable. Then just output as usual.

Then, the operator<<() is overloaded for the case of a clear_line object. So if one of those is sent, we know to flip the is_next_clear flag, but not actually output anything.

Here's an example use:

int main() {
    out o;
    o << "Some real output" << cls;
    o << "Some other real output";
    
    return 0;
}

Here it is in action: https://ideone.com/0Dzwlv


If you want to use endl, you'll need to add a special overload for it as this answer suggests: https://stackoverflow.com/a/1134467/2602718

// this is the type of std::cout
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
out& operator<<(StandardEndLine manip)
{
    // call the function, but we cannot return its value
    manip(strm);
    return *this;
}

Live example: https://ideone.com/ACUMOo

scohe001
  • 15,110
  • 2
  • 31
  • 51
  • That works as shown, but if I change: o << "Some other real output"; to: o << "Some other real output" << endl; gcc gives:error: no match for 'operator<<' (operand types are 'out' and '') – Duncan Moore Dec 30 '20 at 16:58
  • Ahh @Dunc `std::endl` is special for `std::cout`. You can read more here ([std::endl is of unknown type when overloading operator<<](https://stackoverflow.com/a/1134467/2602718)). Essentially you'll need to add a special `endl` for the wrapper. Let me edit to add that... – scohe001 Dec 30 '20 at 17:01
  • @Dunc I've updated to include that example – scohe001 Dec 30 '20 at 17:06