Search code examples
c++operator-overloadinginformation-hidingyaml-cpp

Overloading stream insertion without violating information hiding?


I'm using yaml-cpp for a project. I want to overload the << and >> operators for some classes, but I'm having an issue grappling with how to "properly" do this. Take the Note class, for example. It's fairly boring:

class Note {
  public:
    // constructors
    Note( void );
    ~Note( void );

    // public accessor methods
    void            number( const unsigned long& number ) { _number = number; }
    unsigned long   number( void ) const                  { return _number; }
    void            author( const unsigned long& author ) { _author = author; }
    unsigned long   author( void ) const                  { return _author; }
    void            subject( const std::string& subject ) { _subject = subject; }
    std::string     subject( void ) const                 { return _subject; }
    void            body( const std::string& body )       { _body = body; }
    std::string     body( void ) const                    { return _body; }

  private:
    unsigned long   _number;
    unsigned long   _author;
    std::string     _subject;
    std::string     _body;
};

The << operator is easy sauce. In the .h:

YAML::Emitter& operator << ( YAML::Emitter& out, const Note& v );

And in the .cpp:

YAML::Emitter& operator << ( YAML::Emitter& out, const Note& v ) {
  out << v.number() << v.author() << v.subject() << v.body();
  return out;
}

No sweat. Then I go to declare the >> operator. In the .h:

void operator >> ( const YAML::Node& node, Note& note );

But in the .cpp I get:

void operator >> ( const YAML::Node& node, Note& note ) {
  node[0] >> ?
  node[1] >> ?
  node[2] >> ?
  node[3] >> ?
  return;
}

If I write things like node[0] >> v._number; then I would need to change the CV-qualifier to make all of the Note fields public (which defeats everything I was taught (by professors, books, and experience))) about data hiding.

I feel like doing node[0] >> temp0; v.number( temp0 ); all over the place is not only tedious, error-prone, and ugly, but rather wasteful (what with the extra copies).

Then I got wise: I attempted to move these two operators into the Note class itself, and declare them as friends, but the compiler (GCC 4.4) didn't like that:

src/note.h:44: error: ‘YAML::Emitter& Note::operator<<(YAML::Emitter&, const Note&)’ must take exactly one argument
src/note.h:45: error: ‘void Note::operator>>(const YAML::Node&, Note&)’ must take exactly one argument

Question: How do I "properly" overload the >> operator for a class

  1. Without violating the information hiding principle?
  2. Without excessive copying?

Solution

  • The typical way to do this without violating encapsulation is to make the operator>> a friend function. There must have been a syntax problem with your declaration of a friend operator (not clear what exactly from the error message). I don't use YAML, but from your question the following is the jist of it:

    class Note{
        ...
        friend void operator >> ( const YAML::Node& node, Note& note );
        ....
     };
     void operator >> ( const YAML::Node& node, Note& note ){
        node[0] >> note._number;
        node[1] >> note._author;
        node[2] >> note._subject;
        node[3] >> note._body;
     }
    

    A friend function has the same access rights to private members as a member function.

    Alternatively, you can declare setters for all member data, but the friend function method is cleaner.