mycout automatic endl
Asked Answered
I

4

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

Infiltrate answered 14/12, 2011 at 18:52 Comment(5)
Of course it's possible, do you have a more specific question about writing your own classes and operator overloading?Internuncio
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.Pejoration
@Ed Heal I get task, found solution with temporary object but can't find without it.Infiltrate
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.Bogbean
@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 anythingBogbean
B
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" << "!";
}
Bogbean answered 14/12, 2011 at 20:1 Comment(2)
Can you be certain that copy elision won't occur in the return from operator<<(MyCout&, const T&)?Loki
@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.Bogbean
H
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.

Horsefaced answered 14/12, 2011 at 18:54 Comment(4)
I was thinking that too. Together with move semantics for the inner operations.Leontineleontyne
You can only use each object for one output statement though, that seems extremely suboptimal.Jeffrey
@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 solutionInfiltrate
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";Horsefaced
L
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.

Leontineleontyne answered 14/12, 2011 at 19:2 Comment(2)
@Kerrek SB Ok) It is possible to use C++ without C++11 features?Infiltrate
@eXXXXXXXXXXX: You could try a non-const copy constructor, like auto_ptr. That should work, too.Leontineleontyne
B
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" << "!";
}
Bogbean answered 14/12, 2011 at 20:1 Comment(2)
Can you be certain that copy elision won't occur in the return from operator<<(MyCout&, const T&)?Loki
@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.Bogbean
I
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" << "!";
}
Interesting answered 14/12, 2011 at 19:46 Comment(5)
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.Pejoration
@Rob: This is what I would do, except I would use a bool to see if it's alive instead of hitting the allocator.Bogbean
@ebyrob: Can't return by value, because it might make a copy, and then delete the temporary, causing an extra flush.Bogbean
@MooingDuck: I'm not sure I follow. This seems to work on my system: template<class T> 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?Pejoration
@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 endls.Loki

© 2022 - 2024 — McMap. All rights reserved.