8

I'd like implement class MyCout, which can provide possibility of automatic endl, i.e. this code

MyCout mycout;
mycout<<1<<2<<3;

outputs

123
//empty line here

Is it possible to implement class with such functionality?


UPDATE: Soulutions shouldn't be like that MyCout()<<1<<2<<3; i.e. they should be without creating temporary object

eXXXXXXXXXXX2
  • 1,540
  • 1
  • 18
  • 32
  • Of course it's possible, do you have a more specific question about writing your own classes and operator overloading? – TJD Dec 14 '11 at 18:55
  • This is an interesting problem. As I understand it endl is tied up with flushing. Not to mention that the operator overloading method would have to know something about what happens after it returns to know where the endl belongs. –  Dec 14 '11 at 18:59
  • @Ed Heal I get task, found solution with temporary object but can't find without it. – eXXXXXXXXXXX2 Dec 14 '11 at 19:10
  • Why is a temporary not allowed? I don't think this is possible without a temporary. Wait, I think you're misunderstanding what we say when we say we're using temporaries. Look at Rob's code. – Mooing Duck Dec 14 '11 at 19:49
  • @Rob: to clarify, all the answers use a temporary, but it's is created automatically. All our answers's output lines look just like yours. The caller doesn't have to write `MyCout()<<...` or anything – Mooing Duck Dec 14 '11 at 22:04

4 Answers4

8

You can use the destructor of a temporary object to flush the stream and print a newline. The Qt debug system does this, and this answer describes how to do it.

Community
  • 1
  • 1
James
  • 24,676
  • 13
  • 84
  • 130
  • I was thinking that too. Together with move semantics for the inner operations. – Kerrek SB Dec 14 '11 at 18:56
  • You can only use each object for one output statement though, that seems extremely suboptimal. – Seth Carnegie Dec 14 '11 at 18:58
  • @Autopulated, yes, I know about this variant, but I wrote that it must look following way: `MyCout mycout; mycout<<...` i.e. temporary object isn't solution – eXXXXXXXXXXX2 Dec 14 '11 at 19:02
  • I actually use this (a **lot**) myself, I have a `debug() << "stuff";` system that does variable-level printing with time&thread-stamped logs. If brevity is really a concern, use a single character class name - or even abuse the preprocessor...: `d() << "stuff";` – James Dec 14 '11 at 19:06
7

The following works in C++11:

#include <iostream>

struct myout_base { };
struct myout
{
  bool alive;
  myout() : alive(true) { }
  myout(myout && rhs) : alive(true) { rhs.alive = false; }
  myout(myout const &) = delete;
  ~myout() { if (alive) std::cout << std::endl; }
};

template <typename T>
myout operator<<(myout && o, T const & x)
{
  std::cout << x;
  return std::move(o);
}

template <typename T>
myout operator<<(myout_base &, T const & x)
{
  return std::move(myout() << x);
}

myout_base m_out;   // like the global std::cout

int main()
{
  m_out << 1 << 2 << 3;
}

With more work, you can add a reference to the actual output stream.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
2

This is simply a variant of Rob's answer, that doesn't use the heap. It's a big enough change that I didn't want to just change his answer though

struct MyCout {
  MyCout(std::ostream& os = std::cout) : os(os) {}
  struct A {
    A(std::ostream& r) : os(r), live(true) {}
    A(A& r) : os(r.os), live(true) {r.live=false;}
    A(A&& r) : os(r.os), live(true) {r.live=false;}
    ~A() { if(live) {os << std::endl;} }
    std::ostream& os;
    bool live;
  };
  std::ostream& os;
};

template <class T>
MyCout::A operator<<(MyCout::A&& a, const T& t) {
  a.os << t;
  return a;
}

template<class T>
MyCout::A operator<<(MyCout& m, const T& t) { return MyCout::A(m.os) << t; }

int main () {
  MyCout mycout;
  mycout << 1 << 2.0 << '3';
  mycout << 3 << 4.0 << '5';
  MyCout mycerr(std::cerr);
  mycerr << 6 << "Hello, world" << "!";
}
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • Can you be certain that copy elision won't occur in the return from `operator<<(MyCout&, const T&)`? – Robᵩ Dec 14 '11 at 20:38
  • 1
    @Rob: No, but if I did it right, that doesn't change the behavior of the program. I use `bool live` to emulate a move constructor. – Mooing Duck Dec 14 '11 at 21:31
1

If you need to avoid C++11 features:

#include <iostream>
#include <sstream>
#include <memory>

struct MyCout {
  MyCout(std::ostream& os = std::cout) : os(os) {}
  struct A {
    A(std::ostream& os) : os(os) {}
    A() : os(os) {}
    ~A() { os << std::endl; }
    std::ostream& os;
  };
  std::ostream& os;
};

template <class T>
const std::auto_ptr<MyCout::A>&
operator<<(const std::auto_ptr<MyCout::A>& a, const T& t) {
  a->os << t;
  return a;
}

template<class T>
const std::auto_ptr<MyCout::A>
operator<<(MyCout& m, const T& t) {
  std::auto_ptr<MyCout::A> p(new MyCout::A(m.os));
  p << t;
  return p;
}

int main () {
  MyCout mycout;
  mycout << 1 << 2 << 3;
  mycout << 3 << 4 << 5;
  MyCout mycerr(std::cerr);
  mycerr << 6 << "Hello, world" << "!";
}
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • Yep, just create an inner class and return it by value instead of by reference on the initial << operator, then flush/endl on the destructor of that object. Auto creates the temp object for you. –  Dec 14 '11 at 19:54
  • @Rob: This is what I would do, except I would use a bool to see if it's alive instead of hitting the allocator. – Mooing Duck Dec 14 '11 at 19:56
  • @ebyrob: Can't return by value, because it might make a copy, and then delete the temporary, causing an extra flush. – Mooing Duck Dec 14 '11 at 19:57
  • @MooingDuck: I'm not sure I follow. This seems to work on my system: template const MyCout::A operator<<(MyCout& m, const T& t) { m.os << t; return MyCout::A(m.os); } Is there ambiguity in how return behaves in this context? –  Dec 14 '11 at 20:11
  • 2
    @ebyrob : Try compiling that code with `g++ -fno-elide-constructors test.cpp`. The compiler is allowed to default-construct the temporary inside `operator<<` and then copy-construct the temporary inside `main`. Too many constructors, too many destructors, too many `endl`s. – Robᵩ Dec 14 '11 at 20:21