Шаблонный конструктор C ++ / десериализация - PullRequest
1 голос
/ 25 мая 2010

Я смотрел на библиотеку 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 в функции (указатели), которые возвращают объекты нужного типа (и все в порядке благодаря ковариантным типам).

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

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

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;
}

Я уверен, что это можно сделать (Boost serialize делает это, но его код непроницаем!: 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 );
...

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

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

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

Приветствия

Ответы [ 2 ]

0 голосов
/ 25 мая 2010

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


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

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

<code></p>

<p>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);
  }
};
0 голосов
/ 25 мая 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...