## Monday, October 18, 2010

The operator<< is used to stringify an object and "shift" the result to an output stream, such as std::cout for standard output. It is used like this:
#include <iostream>
int main() {
std::cout << "hello world" << std::endl;
return 0;
}
This of course only works for values of types for which there is an overloading of the operator<<. If you want to implement serialization for a new class, you will have to implement an operator<<() in the global scope, and cannot do so in the class scope. That is because the left hand side of << is an std::ostream& object, not your object.

For example, this is right:
class Foo {...};

std::ostream& operator<<(std::ostream& os, const Foo& foo) {
...
}
But this will not result in the intended notation:
class Foo {
...

std::ostream& operator<<(std::ostream& os) {
// stringify this object to output stream os.
...
}
};
This means that a statement like foo << std::cout will stream foo to std::cout, but the notation is now wrong. You can overload operator>> instead, but this notation has other issues we will not discuss here.

Sometimes we do not want to overload operator<< globally, at least we don't want to pollute the global namespace with a different operator<< for each serializable class. We can do this by introducing an indirection layer with subtyping polymorphism.
class serializable {
public:
virtual void serialize(std::ostream& os) const = 0;
};

std::ostream& operator<< (std::ostream& os, const serializable& s) {
s.serialize(os);
return os;
}

class Foo : public serializable {
public:
virtual void serialize(std::ostream& os) const {
os << "Foo" << std::endl;
}
};

Then we can simply say:
int main() {
Foo f;
std::cout << f;
return 0;
}

We implement exactly one global operator<< trampoline, which calls the serialize() method of a serializable abstract base class. Any subclass of a serializable that implements the serialize() method can now also be used with operator<< automatically.

If you don't want to use virtual table, a similar technique using Curiously Recurring Template Pattern also works.
template<class Derived>
class serializable {
public:
void serialize(std::ostream& os) const {
static_cast<const Derived *>(this)->serialize(os);
}
};

template<class Derived>
std::ostream&
operator<< (std::ostream& os, const serializable<Derived>& s) {
s.serialize(os);
return os;
}

class Foo : public serializable<Foo> {
public:
void serialize(std::ostream& os) const {
os << "Foo" << std::endl;
}
};


We can similarly define unserializable class with operator>> analogously. This is left as an exercise for the reader.

#### 1 comment:

Ant said...

Thank you. Very useful. Ant