How to deserialize polymorphic classes?

Viewed 126

I have abstract base class User which is inherited by Student and Professor. User have virtual functions for writing in file (serializing) which is overridden in derived classes.

void Student::write(std::ofstream& ofs) const {
    ofs << typeid(*this).raw_name() << id << "," << get_name() << "," << get_surname() << ",";
}

void Professor::write(std::ofstream& ofs) const {
    ofs << typeid(*this).raw_name() << title << "," << get_name() << "," << get_surname() << ",";
}

It also has function for reading from file (deserialization). The problem is, when I read typeid(*this).raw_name() from file, I don't know how to access Student or Professor so I can instantiate one of those. I'm using template data structure class, so I can't check that explicitly.

    template <typename T>
    class UndirectedGraph { ...

        void for_each_DFS(int vertex, const std::function<void(const T&)>& func) const {
            for (const auto value : *nodes.at(vertex))
                func(value);
        }

        void for_each_DFS(int vertex, const std::function<void(T&)>& func) {
            for (auto value : *nodes[vertex])
                func(value);
        }

        virtual void write(std::ofstream& ofs) const override {
            if (std::is_pointer<T>::value)
                for_each_DFS(0, [&ofs](const T& obj) { ofs << *obj; });
            else
                for_each_DFS(0, [&ofs](const T& obj) { ofs << obj; });
        }

        virtual void read(std::ifstream& ifs) override {
            std::string type;
            for_each_DFS(0, [&ifs](T& obj) { 
                // here I get that info, but how do I use it do declare type of derived class?
                std::getline(ifs, type, ','); 
                ifs >> obj;
            });
        }
    }
1 Answers

Here is a possible solution using std::any and std::type_index.

#include <any>
#include <string>
#include <typeinfo>
#include <typeindex>
#include <unordered_map>

class A {};
class B {};

template<typename T>
std::string write(){
    static const std::unordered_map<std::type_index, std::string> kTypeNames{{typeid(A), "A"}, {typeid(B), "B"}};
    return kTypeNames.at(typeid(T));
}

std::any read(const std::string& s){
   if(s == "A") return A{};
   if(s == "B") return B{};
   return {};
}
Related