<!--toc--> |
|
## Введение |
|
Часто требуется сохранить некоторые объекты C++ в хранилище данных какого-то типа, вроде пользовательской сессии или кэша и извлечь их обратно. |
|
Это нетривиальная задача для C++ - [нерефлексивного](http://ru.wikipedia.org/wiki/%D0%9E%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29) языка. Поэтому CppCMS обеспечивает простую библиотеку сериализации, которая помогает нам выполнять такие задачи. |
|
## Инструменты Сериализации CppCMS |
### Подготовка Объектов |
|
Предположим, у нас есть класс `person`, содержащий некоторую информацию и список его потомков - классы `child`. |
|
Человек: |
|
struct person { |
std::string name; |
std::string family; |
std::string occupation; |
std::string martial_state; |
double salary; |
physical parameters; |
std::list<child> children; |
std::vector<std::string> friends; |
}; |
|
Потомок: |
|
struct child { |
std::string name; |
physical parameters; |
} |
|
Физические данные о них: |
|
struct physical { |
int age; |
double height; |
}; |
|
|
Теперь, чтобы сериализовать наш объект, мы можем наследовать его от класса `cppcms::serializable`. Добавьте заголовочный файл `<cppcms/serialization.h>` и обновите объект так, чтобы: |
|
struct child { |
... |
}; |
struct person { |
... |
}; |
|
стало |
|
struct child : public cppcms::serializable { |
... |
}; |
struct person : public cppcms::serializable { |
... |
}; |
|
_Примечание:_ Так как объект "physical" - [POD](http://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D1%81%D1%82%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85), мы можем обработать его лучшим образом. |
|
Теперь добавим двум классам выше новый метод, описывающий как их сериализовать: |
|
Потомок: |
|
void serialize(cppcms::archive &a) |
{ |
a & name & cppcms::as_pod(parameters); |
} |
|
Метод получает в качестве параметра `cppcms::archive` - объект, способный сохранить/восстановить нужные данные и мы отмечаем каждый элемент: `name` и `parameters`. |
|
_Примечание:_ поскольку мы знаем, что `parameters` - POD-объект, мы используем специальный маркер, описывающий как его скопировать. |
|
Далее делаем то же самое для класса `person`: |
|
void serialize(cppcms::archive &a) |
{ |
a & name & family & occupation & martial_state |
& salary & cppcms::as_pod(parameters) & children & friends; |
} |
|
**Замечание:** |
|
Объект `archive` знает, как обращаться со стандартными C++-контейнерами типа `std::vector` или `std::string`. Он знает, как сохранить примитивные типы вроде `double` в виде простых двоичных данных. |
|
Потому, например, т.к. `children` это `std::list<child>` и `child` наследуется от `cppcms::serializable`, он знает как автоматически справиться с этой ситуацией. |
|
Если Вы используете собственные или нестандартные контейнеры, возможно потребуется специализация: [`cppcms::archive_traits`](/cppcms_ref/1.0.2/structcppcms_1_1archive__traits.html) класса для Вашего конкретного класса. |
Если Вы используете собственные или нестандартные контейнеры, возможно потребуется специализация класса: [`cppcms::archive_traits`](/cppcms_ref/1.0.2/structcppcms_1_1archive__traits.html) для Вашего конкретного класса. |
|
|
### Сохранение и Восстановление Объектов |
|
|
Предположим, что `std::string buffer` - наш контейнер данных. |
|
Таким образом, мы можем сохранить данные в буфер с помощью следующих действий: |
|
person john; |
... // проинициализировать его данные |
cppcms::archive a; |
john.save(a); // сохранить их в архив |
buffer = a.str(); // взять буфер |
|
Мы можем загрузить их так: |
|
person john; |
cppcms::archive a; |
a.str(buffer); |
john.load(a); |
|
Это очень простой способ сохранения и загрузки данных, но мы рекомендуем использовать чуть более сложное API, которое даст нам больше возможностей, как увидим позже: |
Это простейший способ сохранения и загрузки данных, но мы рекомендуем использовать чуть более сложное API которое, как увидим позже, даст нам больше возможностей: |
|
Save: |
|
person john; |
... // setup its data |
cppcms::serialization_traits<person>::save(john,buffer); |
|
Load: |
|
person john; |
cppcms::serialization_traits<person>::load(buffer,john); |
|
|
|
### Использование с Кэшем и Сессиями |
|
Теперь давайте посмотрим, как мы можем использовать инструменты сериализации CppCMS с интерфейсами Кэша и Сессий: |
|
Можем легко закэшировать объекты `person` следующим образом: |
|
person john; |
if(!cache().fetch_data("john",john)) { |
init_person(john); |
cache().store_data("john",john); |
} |
|
Это бы позволило, например, снизить число сложных запросов к базе данных для некоторых общих данных, которые не обновлялись. |
|
Можем сохранить объекты с помощью сессий: |
|
person me; |
if(session().is_set("me")) { |
session().fetch_data("me",me); |
me.salary += 1; |
session().store_data("me",me); |
} |
|
|
## Использование Стороннего API Сериализации |
|
### Принцип |
|
CppCMS API - простое, быстрое и мощное, но сильно зависит от платформы - оно не позволяет передать один и тот же объект между хостами с различной архитектурой или порядком следования байт, не поддерживает версионность и прочее. |
|
Поэтому, в некоторых случаях может потребоваться использование с CppCMS сторонних API. |
|
Это довольно просто, все что нужно сделать - предоставить специализацию класса `serialization_traits` - вот в чем и была причина, по которой мы рекомендовали использовать класс `serialization_traits`, вместо прямого использования `cppcms::archive`. |
|
### Boost.Serialization |
|
Давайте посмотрим, как использовать, например, Boost.Serialization с CppCMS. |
|
Это наш простейший класс, поддерживающий Boost.Serialization: |
|
|
struct person { |
|
std::string name; |
std::string family; |
|
template<typename Archive> |
void serialize(Archive &a,unsigned /*version*/) |
{ |
a & name & family; |
} |
}; |
|
Давайте определим две небольшие вспомогательные функции для сохранения и загрузки объекта в строку: |
|
template<typename T> |
void load_from_boost(std::string const &src,T &obj) |
{ |
std::stringbuf ss(src); |
{ |
boost::archive::binary_iarchive oa(ss); |
oa >> obj; |
} |
} |
|
|
template<typename T> |
void save_with_boost(T const &obj,std::string &tgt) |
{ |
std::stringbuf ss; |
{ |
boost::archive::binary_oarchive oa(ss); |
oa << obj; |
} |
tgt=ss.str(); |
} |
|
Теперь все, что нам нужно, это специализация нашего `serialization_traits` для корректной обработки `person`: |
|
namespace cppcms { |
template<> |
struct serialization_traits<person> { |
static void save(person const &obj,std::string &tgt) |
{ |
save_with_boost(obj,tgt); |
} |
static void load(std::string const &src,person &obj) |
{ |
load_from_boost(src,obj); |
} |
}; |
} |
|
|
Тогда Вы можете свободно сохранять и загружать объект с помощью CppCMS интерфейса сессии или кэша: |
В дальнейшем можно будет свободно сохранять и загружать объект с помощью CppCMS интерфейса сессии или кэша: |
|
person john; |
if(!cache().fetch_data("john",john)) { |
init_person(john); |
cache().store_data("john",john); |
} |
|
|
|
|