The way to implement this logic is to create a custom stream buffer which writes at the opportune location in its overflow()
method and then to create a suitable std::ostream
using this stream buffer. Here is a rough sketch on how that would look like:
class posbuf: public std::streambuf {
int d_row; // current row
int d_column; // column where to start output
void move() { /* use curses, VT100 codes, whatever to move the cursor */ }
int overflow(int c) {
if (c == traits_type::eof()) { return traits_type::not_eof(c); }
if (c == '\n') { ++this->d_row; this->move(); return '\n'; }
return std::cout << traits_type::to_char_type(c)? c: traits_type::eof();
}
int sync() {
return std::cout.flush()? 0: -1;
}
public:
posbuf(int row, int column): d_row(row), d_column(column) { this->move(); }
};
struct oposstream: private virtual posbuf, public std::ostream {
oposstream(int row, int column): posbuf(row,c column), std::ostream(this) {}
};
int main() {
oposstream out(4, 10);
out << "hello\nworld";
}
Except for a couple of likely typos and the details on how to actually move the cursor, the above code should actually work and create an std::ostream
which can be passed wherever an std::ostream&
is expected. Any attempt to overload the output operator using some templates will not work as well.