tags:

views:

230

answers:

2

Hi!

I want to make a class based on "ostream" that does some auto-formatting to generate comma- or tab-separated-value files. My idea was to override "operator<<" to have it insert a separator before each value (except at the beginning and end of a line), and also to quote strings before writing them. Within the overriding "operator<<" method, I wanted to call the method of the base class, but I can't get it to work right.

Here's an example (compiles with g++ 4.3.3):

#include <iostream>
#include <ostream>
#include <string>

using namespace std;

class MyStream: public ostream
{
public:
  MyStream(ostream& out): ostream(out.rdbuf()) {}

  template <typename T> MyStream& operator<<(T value)
  {
    ostream::operator<<('+');
    ostream::operator<<(value);
    ostream::operator<<('+');
    return *this;
  }
};

template<> MyStream& MyStream::operator<< <string>(string value)
{
  ostream::operator<<('*');
  ostream::write(value.c_str(), value.size()); // ostream::operator<<(value);
  ostream::operator<<('*');
  return *this;
}

int main()
{
  MyStream mystr(cout);
  mystr << 10;
  cout << endl;
  mystr << "foo";
  cout << endl;
  mystr << string("test");
  cout << endl;
  return 0;
}

The two "operator<<" methods (template and specialization) are there to handle strings differently than everything else. But:

  1. The characters ('+'/'*') are printed as numbers and not characters.
  2. The C-String "foo" prints as a memory address (I think).
  3. If the "write" line is exchanged with the commented part, the compiler complains that there's "no matching function for call to 'MyStream::operator<<(std::string&)'", even though I thought I was explicitly calling the base class method.

What am I doing wrong? Any help greatly appreciated.

+4  A: 

The operator<< overloads that prints strings and characters are free functions. But as you force calling member functions, you will force them to convert to one candidate of the member functions declared in ostream. For '*', it will probably use the int overload, and for "foo", it will probably use the const void* overload.

I would not inherit ostream, but instead store the ostream as a reference member, and then delegate from your operator<< to it. I would also not make operator<< a member, but rather a free function template, and not specialize but overload the operator<< for both std::string and char const*.

Johannes Schaub - litb
+1  A: 

Something like the following might work:

include

#include <ostream>
#include <string>

using namespace std;

class MyStream: public ostream
{
public:
    MyStream(ostream& out): ostream(out.rdbuf()) {}

    template <typename T> MyStream& operator<<(const T& value)
    {
        (ostream&)*this << '+' << value << '+';
        return *this;
    }
    MyStream& operator<< (const string& value)
    {
        (ostream&)*this << '*' << value << '*';
        return *this;
    }
    MyStream& operator<< (const char* cstr)
    {
        (ostream&)*this << '(' << cstr << ')';
        return *this;
    }
};
UncleBens
Thanks for you comment on my post, which I've now deleted - I get the gist of the requirement from the OP now.
quamrana
wrt your comment on quamrana's question where you ask "Why exactly wouldn't you want to substitute MyStream where ostream is accepted?": Inheriting doesn't give any goods here, i think. It just brings several dangers. Since the various `operator<<` in `ostream` are all non-virtual, passing a `MyStream` to where a `ostream`.
Johannes Schaub - litb