views:

108

answers:

1

The following code gives an error when it's supposed to output just std::endl:

#include <iostream>
#include <sstream>

struct MyStream {
  std::ostream* out_;
  MyStream(std::ostream* out) : out_(out) {}
  std::ostream& operator<<(const std::string& s) {
    (*out_) << s;
    return *out_;
  }
};

template<class OutputStream>
struct Foo {
  OutputStream* out_;
  Foo(OutputStream* out) : out_(out) {}
  void test() {
    (*out_) << "OK" << std::endl;
    (*out_) << std::endl; // ERROR     
  }
};

int main(int argc, char** argv){
  MyStream out(&std::cout);
  Foo<MyStream> foo(&out);
  foo.test();
  return EXIT_SUCCESS;
}

The error is:

stream1.cpp:19: error: no match for 'operator<<' in '*((Foo<MyStream>*)this)->Foo<MyStream>::out_ << std::endl'
stream1.cpp:7: note: candidates are: std::ostream& MyStream::operator<<(const std::string&)

So it can output a string (see line above the error), but not just the std::endl, presumably because std::endl is not a string, but the operator<< definition asks for a string.

Templating the operator<< didn't help:

  template<class T>
  std::ostream& operator<<(const T& s) { ... }

How can I make the code work? Thanks!

+5  A: 

You need to add this to your struct MyStream:

  std::ostream& operator<<( std::ostream& (*f)(std::ostream&) )
  {
      return f(*out_);
  }

std::endl is a function that appends a newline and flushes the underlying stream; this function signature accepts that function and applies it to the ostream member.

Then, as a test, defining foo::test as

  void test() {
    (*out_) << "start";
    (*out_) << std::endl;
    (*out_) << "done";
  }

will correctly output

start
done
Mark Rushakoff
Thanks, that's pretty crazy. How is one supposed to know that? There should be some class that one can derive from that would add such unsightly stuff automatically.
The thing to remember with overloading iostream operators is const char* and this function pointer. This is because the C++ template system has some special rules about pointer types. (They're ambiguous, I think?)
Zan Lynx
@dehmann: I have **no** idea how anyone was supposed to know that. I had to try a few different Google searches to get *pointed* in the right direction, then I *still* had to piece the answer together. Hopefully this helps a few more people in this situation.
Mark Rushakoff
+1 for an awesome piece of detective work!
Billy ONeal
It's not a function, but it's a function *template* (so that it's not specific to `ostream`, but also works for `wostream` and others). That's why a simple `T` doesn't work.
Johannes Schaub - litb