Конструктор/десериализация фабрики по шаблону С++

Я смотрел на библиотеку сериализации boost, и навязчивый способ обеспечить поддержку сериализации - определить функцию-член с подписью (упрощение):

class ToBeSerialized {
public:
    //Define this to support serialization
    //Notice not virtual function!
    template<class Archive>
    void serialize(Archive & ar)
    {.....}
};

Более того, один из способов поддержки сериализации производных классов через базовые указатели — использовать макрос типа:

//No mention to the base class(es) from which Derived_class inherits
BOOST_CLASS_EXPORT_GUID(Derived_class, "derived_class")

где Derived_class — это некоторый класс, который наследуется от базового класса, скажем, Base_class. Благодаря этому макросу можно корректно сериализовать классы типа Derived_class через указатели на Base_class.

Вопрос в следующем: я использую C++ для написания абстрактных фабрик, реализованных через карту из std::string в (указатель на) функции, которые возвращают объекты нужного типа (и все в порядке благодаря ковариантным типам).

Hover Я не вижу, как я мог бы использовать приведенную выше функцию-член шаблона невиртуальной сериализации для правильной десериализации (т.е. создания) объекта, не зная его типа (но предполагая, что информация о типе была сохранена сериализатором, скажем, в нить).

То, что я хотел бы сделать (сохраняя ту же номенклатуру, что и выше), выглядит примерно так:

XmlArchive xmlArchive; //A type or archive
xmlArchive.open("C:/ser.txt"); //Contains type information for the serialized class
Base_class* basePtr = Factory<Base_class>::create("derived_class",xmlArchive);

с функцией справа, создающей объект в куче типа Derived_class (с помощью конструктора по умолчанию, это та часть, которую я знаю, как решить) и вызывая функцию сериализации xmlArchive (здесь я застрял!), т.е. что-то типа:

Base_class* Factory<Base_class>::create("derived_class",xmlArchive)
{
    Base_class* basePtr = new Base_class; //OK, doable, usual map string to pointer to function
    static_cast<Derived_class*>( basePtr )->serialize( xmlArchive ); //De-serialization, how?????
    return basePtr;
}

Я уверен, что это можно сделать (ускоренная сериализация делает это, но его код непроницаем! :P), но я не могу понять, как это сделать. Основная проблема заключается в том, что функция сериализации является шаблонной функцией. Поэтому у меня не может быть указателя на общую шаблонную функцию. Поскольку смысл написания шаблонной функции сериализации состоит в том, чтобы сделать код универсальным (т.е. не нужно переписывать функцию сериализации для разных Архиваторов), нет смысла регистрировать все производные классы для всех возможных типов архивов. , как:

MY_CLASS_REGISTER(Derived_class, XmlArchive);
MY_CLASS_REGISTER(Derived_class, TxtArchive);
...

На самом деле в моем коде я полагаюсь на перегрузку, чтобы получить правильное поведение:

void serialize( XmlArchive& archive, Derived_class& derived );
void serialize( TxtArchive& archive, Derived_class& derived );
...

Ключевым моментом, о котором следует помнить, является то, что тип архива всегда известен, т.е. я никогда не использую полиморфизм времени выполнения для класса архива... (опять же я использую перегрузку для типа архива).

Любое предложение, чтобы помочь мне?

Заранее большое спасибо!

Ваше здоровье


person StephQ    schedule 24.05.2010    source источник


Ответы (2)


Все, что вам нужно, это сохранить какой-то идентификатор перед сохранением информации из производного типа. Затем после чтения вы используете этот идентификатор, который вы прочитали первым, чтобы направить вас к фабрике, которая затем может правильно интерпретировать следующий блок информации и сгенерировать ваш производный тип. Это, вероятно, то, что boost::serialization делает на самом базовом уровне. Может как-то так:


ar >> type;
Base_class* basePtr = Factory<Base_class>::create(type,xmlArchive);

Тогда у вас есть карта объектов, которая выглядит примерно так:

struct reader_base { virtual void load(xmlArchive, base_ptr) = 0; } template < typename T > struct reader : reader_base { virtual void load(xmlArchive, base_ptr) { static_cast<T*>(base_ptr)->serialize(xmlArchive); } };

person Edward Strange    schedule 24.05.2010
comment
Но как мне сделать этот код универсальным по отношению к типу xmlArchive? Я хотел бы иметь единую регистрацию (макрос как ускорение или улучшенную функцию) для каждого производного класса, и чтобы каждый тип архива мог делать то, что ему нужно, на основе информации, предоставленной только этой регистрацией. Спасибо за вашу помощь в любом случае! - person StephQ; 25.05.2010
comment
Просто измените приведенные выше концепции, чтобы они также были основаны на архиве. Ничего страшного. - person Edward Strange; 25.05.2010

Если тип вашего архива всегда известен, зачем заморачиваться параметризацией вашей функции serialize для него? Да, есть аргумент в пользу повторного использования кода, но если ваш класс Archive когда-либо изменит свое определение или будет заменен, вам, вероятно, все равно придется реорганизовать часть вашего кода сериализации.

Если вы придерживаетесь:

class ToBeSerialized : public Base_class {
public:
    void serialize(Archive & ar)
    {.....}
};

Затем вы можете взять указатель на свою функцию serialize и привязать ее к своей фабрике.

Вам также потребуется привязать отдельные функции create для каждого класса, чтобы он мог создавать экземпляры нужного типа, когда его об этом просят. Что-то типа:

template <typename T> Base_class* Factory::create(Archive& xmlArchive) {
    T* derivedPtr = new T;
    derivedPtr->serialize( xmlArchive );
    return derivedPtr;
}

Затем фабрике потребуется общий метод create, который вызывает правильный статический параметризованный create<T>:

Base_class* Factory::create(const char* typeString, Archive& xmlArchive) {
    // Pseudocode.
    return m_map.find(typeString)->callCreate(xmlArchive);
}
person Blair Holloway    schedule 24.05.2010
comment
У меня проблема именно в том, что тип архива является явным, но не фиксированным, я могу сказать, что это Xml-архив, двоичный архив и так далее... Дело в том, что функция сериализации довольно глупая, например: шаблон‹класс A› void serialize( A& a ) { serialize(a, m_int, m_int ); сериализовать( а, m_double, m_double ); } На самом деле он просто перечисляет элементы данных, которые необходимо сериализовать, и связывает строку с каждым из них. Затем serialize( a, m_int, m_int ) выполняет сериализацию на основе типов a (архив) и m_int (скажем, int для сериализации). Извините за недопонимание. - person StephQ; 25.05.2010