#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:
Thank you. Very useful. Ant
Post a Comment