昨日書いた仮想コンストラクタのコード、何度読み返してみても気持ちが悪い。
ためしに、デシリアライザで書き直してみた。
やっぱりこっちの方が素直で間違いもない気がする。
少なくともわたしには、トリッキーなコードを書くだけのメリットが感じられず。
…読み進めるとなにかあるのかな…?
以下コード。
#include <iostream> #include <sstream> #include <string> #include <map> #include <stdexcept> class Base { public: virtual ~Base() {} virtual void write(std::ostream& out) const = 0; }; class Point : public Base { public: static const char name[]; Point(std::istream& in) { in >> x_ >> y_; } void write(std::ostream& out) const { out << name << " " << x_ << " " << y_; } private: int x_; int y_; }; class Rectangle : public Base { public: static const char name[]; Rectangle(std::istream& in) { in >> x_ >> y_ >> width_ >> height_; } void write(std::ostream& out) const { out << name << " " << x_ << " " << y_ << " " << width_ << " " << height_; } private: int x_; int y_; int width_; int height_; }; class Circle : public Base { public: static const char name[]; Circle(std::istream& in) { in >> x_ >> y_ >> r_; } void write(std::ostream& out) const { out << name << " " << x_ << " " << y_ << " " << r_; } private: int x_; int y_; int r_; }; std::ostream& operator << (std::ostream& out, const Base& base) { base.write(out); return out; } const char Point::name[] = "point"; const char Rectangle::name[] = "rectangle"; const char Circle::name[] = "circle"; // ストリームからオブジェクトを生成するクラステンプレート template <class BASE> class Deserializer { public: template <class DERIVED> void registerClass() { creators_[DERIVED::name] = create<DERIVED>; } BASE* load(std::istream& in) const { std::string type; in >> type; typename Creators::const_iterator i = creators_.find(type); if(i == creators_.end()) { throw std::runtime_error("unknown type"); } return i->second(in); } private: typedef std::map<std::string, Base* (*)(std::istream&)> Creators; template <class DERIVED> static BASE* create(std::istream& in) { return new DERIVED(in); } Creators creators_; }; // テスト void test(const Deserializer<Base>& deserializer) { std::istringstream pointSource("point 10 20"); Base* p = deserializer.load(pointSource); std::cout << *p << std::endl; delete p; std::istringstream rectangleSource("rectangle 10 20 30 40"); p = deserializer.load(rectangleSource); std::cout << *p << std::endl; delete p; std::istringstream circleSource("circle 100 200 300"); p = deserializer.load(circleSource); std::cout << *p << std::endl; delete p; } int main(int argc, char* argv[]) { Deserializer<Base> deserializer; deserializer.registerClass<Point>(); deserializer.registerClass<Rectangle>(); deserializer.registerClass<Circle>(); test(deserializer); return 0; }
実行結果。
point 10 20 rectangle 10 20 30 40 circle 100 200 300
追記。こうした方が少し便利かもしれない。
template <class BASE> class Deserializer { // ... BASE* load(std::istream& in) const { if(in.bad()) { return 0; } std::string type; in >> type; typename Creators::const_iterator i = creators_.find(type); if(i == creators_.end()) { throw std::runtime_error("unknown type"); } return i->second(in); } // ... }; void test(const Deserializer<Base>& deserializer) { Base* p; std::istringstream source("point 10 20 rectangle 10 20 30 40 circle 100 200 300"); while(source.good() && (p = deserializer.load(source))) { std::cout << *p << std::endl; delete p; } }