#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