3

Since many students I work with on common code have some problems comprehending proper stream operator overloading, I tried to create a helper template (don't know if this is a real mixin) to facilitate the code and insure correct operator implementation. Here it comes:

template<typename T> struct IoEnabled {
  friend std::ostream& operator<<(std::ostream& out, T const& val) {
    return val.print(out);
  }

  friend std::istream& operator>>(std::istream& in, T& val) {
    return val.scan(in);
  }

  friend QTextStream& operator<<(QTextStream& out, T const& val) {
    return val.print(out);
  }

  friend QTextStream& operator>>(QTextStream& in, T& val) {
    return val.scan(in);
  }

  friend QDebug operator<<(QDebug dbg,T const& val){
    std::stringstream myStream;
    myStream << val;
    dbg.nospace() << myStream.str().c_str();
    return dbg;
  }
};

Inheriting class:

class Foo: private IoEnabled<Foo> {
  protected:
   int mData;

  public:
    template<typename U>
    U& scan(U& in) {
      in >> mData;
      return in;
    }

    template<typename U>
    U& print(U& out) const {
      out << mData;
      return out;
    }
}

The downsides of this implementation as far as I see them at the moment:

  • Does not work for 3rd party types
  • Includes inheritance and thus tight coupling with the IoClass although not every user might need Io for a certain type

The ups:

  • It works ;-)
  • Similar stream classes can be added without modifying all classes and withou writing specific new code for every class

Since I'm not very experienced in the usage of mixins and tend to violate coding guidelines once in a while, I'd like to know if this is an appropriate usage of a mixin or how one could obtain a similar effect with another more suitable technique.

Many Thanks, Martin

Martin
  • 4,738
  • 4
  • 28
  • 57

1 Answers1

2

If they can write scan and print template functions, they might as well write templated operators directly, skipping this entire silly mixin business.

struct Foo {
    int mData;
    Foo() : mData(mData) {}
};

template <typename OutputStream>
OutputStream& operator<<(OutputStream& stream, const Foo& data) {
    stream << data.mData;
    return stream;
}

template <typename InputStream>
InputStream& operator>>(InputStream& stream, Foo& data) {
    stream >> data.mData;
    return stream;
}

Also, that special-cased QDebug overload looks entirely unnecessary and wrong.

Cat Plus Plus
  • 125,936
  • 27
  • 200
  • 224
  • IMHO: Fully templated stream operators like you propose can get you into big trouble as I was told some time ago and experienced too http://stackoverflow.com/questions/4195764/is-there-a-warning-free-template-function-to-convert-basic-types-to-string. – Martin Oct 12 '11 at 16:41
  • Further QDebug is quite handy to use for debugging (no chache, red color, all QT DataTypes already supported ...). Unfortunately it cannot be overloaded using the normal syntax. To write "qDebug() <<" as proposed in the qt framework one has to go the way proposed in http://doc.qt.nokia.com/latest/qdebug.html. – Martin Oct 12 '11 at 16:49
  • Also for your templates you will still have to call accessors or make them friends. – Martin Oct 12 '11 at 16:52