#include <iostream.h>
#include <typeinfo>
#include <map>
#include <string.h>
#include <fstream.h>

class root {
public:
  virtual ~root() {};
  virtual char* name() { return "root"; };
};
class lchild : public root {
public:
   char* name() { return "lchild";};
};
class rchild : public root {
public:
  char* name() { return "rchild";};
};
class arc : public lchild, public rchild {
public:
  char* name() { return "arc";};
};

class persistent_store;

class persistent
{
protected:
  virtual void store(ostream&) const = 0;
  virtual void retrieve(istream&) = 0;
private:
  friend class persistent_store;
};

template <class T>
persistent* persistent_object_creator(void)
{
  return new T;
}

template <>
struct less<const char*>
{
  int operator()(const char* p1, const char* p2) { return
strcmp(p1,p2)<0;};
};

class persistent_store
{
  typedef map<const char*, persistent* (*)(void)> maptype;
public:
  persistent_store(iostream& stream) : medium(stream) {};
  template <class T>
  void register_class() {
    persistent* (*func)(void) =  persistent_object_creator<T>;
    creator_map.insert(make_pair(typeid(T).name(),func));
  };
  void store_object(persistent* p) {
    const char* name=typeid(*p).name();
    maptype::iterator iter=creator_map.find(name);
    if (iter == creator_map.end()) throw "unregistered type";
    medium << strlen(name) << ' ' << name;
    p->store(medium);
  };
  persistent* retrieve_object(void) {
    size_t len;
    medium >> len;
    medium.get(); // read past blank.
    char* name=new char[len+1];
    medium.read(name,len);
    maptype::iterator iter=creator_map.find(name);
    delete[] name;
    if (iter == creator_map.end()) throw "unregistered type";
    persistent* p=(iter->second)();
    p->retrieve(medium);
    return p;
  };
private:
  maptype creator_map;
  iostream& medium;
};

class proot : public root, public persistent
{
protected:
  virtual void store(ostream&) const {};
  virtual void retrieve(istream&) {};
};

class parc : public arc, public persistent
{
protected:
  virtual void store(ostream&) const {};
  virtual void retrieve(istream&) {};
};

int main(int argc, char* argv[])
{
  fstream file(argv[1], ios::in | ios::out);
  persistent_store store(file);
  store.template register_class<proot>();
  store.template register_class<parc>();
  if (argc == 2) {
    cout << "Storing" << endl;
    store.store_object(new proot());
    store.store_object(new parc());
    store.store_object(new proot());
    lchild* pl=new parc();
    store.store_object(dynamic_cast<parc*>(pl));
  } else {
    cout << "Retrieving" << endl;
    cout << typeid(*store.retrieve_object()).name() << endl;
    cout << typeid(*store.retrieve_object()).name() << endl;
    cout << typeid(*store.retrieve_object()).name() << endl;
    cout << typeid(*store.retrieve_object()).name() << endl;
  }
}
