Как стать автором
Обновить

Комментарии 18

Использование wide-литералов и std::wstring — так себе идея.
Взамен специализации шаблона функции в данном случае лучше прибегнуть к перегрузке и не передавать builtin типы по ссылке.
Явный вызов new/delete рядом с лямбда-выражениями и прочими C++11 фишками выглядит дико :-)
Простите, не там ответил на комментарий — ответ
Использование wide-литералов и std::wstring — так себе идея.

не передавать builtin типы по ссылке

Хотелось бы узнать поподробнее, чем плохи wide-литералы, и как Вы бы предпочли сериализовать/десериализовать встроенные типы?

Явный вызов new/delete рядом с лямбда-выражениями и прочими C++11 фишками выглядит дико :-)

А как Вам вызов malloc в PrototypeFactory?
Про wchar_t есть неплохой ответ по ссылке stackoverflow.com/questions/11107608/whats-wrong-with-c-wchar-t-and-wstrings-what-are-some-alternatives-to-wide
В данном случае они как минимум являются преждевременной пессимизацией, так как зачастую sizeof(wchar_t) > sizeof(char), а все используемые в идентификаторах C++ символы помещаются в обычный char.
Про встроенные типы, я предлагаю заменить первое объявление на второе:
template <> void serialize (int& value, const wstring& name)
void serialize(const int value, const wstring& name)

Применение malloc/realloc оправдано, там где это нужно (свои аллокаторы, еще какие-то нужды). В данном коде необходимость malloc'a мне не ясна. Более того, судя по всему вы выделяете память под произвольный тип T и «инициализируете» его неким прототипом с помощью memcpy, а что если тип T не является trivially-copyable (например он строка, или любой другой тип, владеющий какими-то динамическими ресурсами)? Или десериализация нетривиальных типов не предполагалась?

Кстати, вот тут exception-unsafety имеется:
std::vector<BaseClass*> vec_ptr = { new BaseClass, new BaseClass, new MyConcreteClass, new SomeonesConcreteClass };
Любое исключение приведет к утечке памяти.
Применение malloc/realloc оправдано, там где это нужно (свои аллокаторы, еще какие-то нужды). В данном коде необходимость malloc'a мне не ясна. Более того, судя по всему вы выделяете память под произвольный тип T и «инициализируете» его неким прототипом с помощью memcpy, а что если тип T не является trivially-copyable (например он строка, или любой другой тип, владеющий какими-то динамическими ресурсами)? Или десериализация нетривиальных типов не предполагалась?

При десериализации объекта мы, зная имя класса этого объекта, должны будем получить участок памяти равный размеру объекта этого класса. В PrototypeFactory мы его и получим. Естественно это будет точная копия объекта, созданного конструктором по умолчанию. Остальную работу по наполнению его содержимым должен будет проделать объект, получивший этот участок памяти.
Где этот код? Что будет с ним, если memcpy скопирует объект, владеющий какими-либо ресурсами?
void serialize (iSerializable*& object, const wstring& name)
{
if (!object) m_factory->get_object (object, m_node.find_child_by_attribute (L«name», name.c_str ()).name ());
object->serialize (get_node_by_attr (name));
}
— собственно пример использования из статьи.

Что касается ресурсов, то если конструктор класса по умолчанию создает объекты в динамической памяти, то это поле должно быть перезадано в полученном таким образом объекте.
Предполагается, что если мы используем для создания объектов класса PrototypeFactory, то мы должны обеспечить ему простой конструктор, не создающий ресурсов, и все данные будем вводить в объект после получения его из PrototypeFactory. Естественно в этом случае выделять память в конструкторе по умолчанию бессмысленно, так как она будет лежать в прототипе безо всякой пользы.
Про встроенные типы, я предлагаю заменить первое объявление на второе:
template <> void serialize (int& value, const wstring& name)
void serialize(const int value, const wstring& name)

Но в таком случае мы не сможем изменить значение данных класса при десериализации.
Ок, не заметил, что ссылка не на константу. А почему сериализация модифицирует объект?
Потому что в общем случае мы не можем знать что будет происходить — сериализация или десериализация.
Почему бы просто не взять буст?

#include <boost/archive/text_oarchive.hpp> 
#include <boost/archive/text_iarchive.hpp> 
#include <iostream> 
#include <sstream>  

class MyConcreteClass 
{ 
public: 
  MyConcreteClass() 
  { 
  } 

  MyConcreteClass (float a, double b) 
    : mFloat(a) 
    , mDouble(b)
  { 
  } 

  float getFloat() const
  {
    return mFloat;
  }
  
  double getDouble() const
  {
    return mDouble;
  }

private: 
  friend class boost::serialization::access; 

  template <typename Archive> 
  void serialize(Archive &ar, const unsigned int version) 
  { 
    ar & mFloat; 
    ar & mDouble;
  } 

  float mFloat;
  double mDouble;
}; 

template<typename T>
std::string serialize(T* object)
{
  std::stringstream sss; 
  boost::archive::text_oarchive oa(sss); 
  oa << object; 
  return sss.str();
}

template<typename T>
T* deserialize(const std::string& object) 
{ 
  std::stringstream sss(object);
  boost::archive::text_iarchive ia(sss); 
  T* retval; 
  ia >> retval; 
  return retval;
} 

int main() 
{ 
  MyConcreteClass* mcc = new MyConcreteClass( 55.4f, 23.9 );
  auto str = serialize(mcc);
  std::cout << str << std::endl;
  
  MyConcreteClass* dmcc = deserialize<MyConcreteClass>(str);
  std::cout << "float: " << dmcc->getFloat() << "; double: " << dmcc->getDouble() << std::endl; 
} 


(понятно, что оформлено все на скорую руку, но я примера ради)
Написал этот код я просто из спортивного интереса, а статью решил написать после прочтения этой ветки обсуждения статьи, также посвященной сериализации, решив, что кому-нибудь это покажется любопытным.
Самое интересное, зачем здесь полиморфизм? Зачем сувать vtable туда, где этого не требуется? Всё же это C++, а не Java или C#, принципы разработки отличаются. Да и уж хотя бы использовали бы CRTP, тогда код будет более эффективным.
Уже достаточно давно заинтересовался темой сериализации, а если конкретно, то сериализацией объектов, хранящихся по указателю на базовый класс.
Тут объекты полиморфны сами по себе по условию задачи.
Всё же это C++, а не Java или C#


Если Вы помните, C++ предоставляет множество парадигм и абсолютно не ограничивает их использование и даже смешивание. Конечно есть некий оверхед в использовании динамического полиморфизма. Но чем конкретно Вам не нравится подобный подход?
Только недавно узнал об этой библиотеке, и ещё в ней не разобрался.

Не так сложна сериализация, как страшна десериализация.
(с) Джейсон Стетхем

Очень непонятно в использовании.

На мой взгляд десериализатор должен выплевывать конкретный дочерний объект по указателю на базовый. При этом при добавлении нового конкретного типа никаких лишних телодвижений в базовых компонентах производиться не должно. Не должно быть никаких ограничений в архитектуре и составе конкретных типов, кроме необходимости реализовать то-то, то-то и сё-то (компилятор должен подсказать выводимыми ошибками).
Но по-моему, это из области фантастики и вообще C++ - боль.

Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории