oess_1: Атрибуты-указатели: их назначение и использование

Назначение атрибутов-указателей

Полиморфные типы

Атрибуты-указатели в ObjESSty поддерживаются для предоставления возможности сериализации объектов полиморфных типов. В некоторых случаях не известно заранее, какой объект будет артрибутом сериализуемого объекта. Известно только, какому базовому типу он принадлежит:

// Базовый тип для всех изображений.
class image_t
  : public oess_1::stdsn::serializable_t
  {
    OESS_SERIALIZER( image_t )
  public :
    virtual ~image_t();
    ...
  };

// Тип, который использует изображения.
class scheme_item_t
  : public oess_1::stdsn::serializable_t
  {
    OESS_SERIALIZER( scheme_item_t )
  private :
    // Подпись под элементом.
    std::string m_title;
    // Изображение для элемента.
    image_t * m_image;

  public :
    ...
  };

Что на DDL представляется следующим образом:

{type	image_t
	|| Тег {super} не указывается, т.к. тип производен от serializable_t.
	...
}

{type	scheme_item_t
	{attr m_title {of std::string}}
	{attr m_image {of {ptr} image_t}}
}

Со временем может быть создано много классов для представления изображений:

// Базовый тип для растровых изображений.
class raster_image_t
  : public image_t
  {
    OESS_SERIALIZER( raster_image_t )
  ...
  };

// Изображение в формате GIF.
class gif_image_t
  : public raster_image_t
  {
    OESS_SERIALIZER( gif_image_t )
  ...
  };

// Изображение в формате JPEG.
class jpeg_image_t
  : public raster_image_t
  {
    OESS_SERIALIZER( jpeg_image_t )
  ...
  };

// Базовый тип для векторных изображений.
class vector_image_t
  : public image_t
  {
    OESS_SERIALIZER( vector_image_t )
  ...
  };

// Изображение в формате SVG.
class svg_image_t
  : public vector_image_t
  {
    OESS_SERIALIZER( svg_image_t )
  ...
  };

Что на DDL представляется следующим образом:

{type	raster_image_t
	{super image_t}
	...
}

{type	gif_image_t
	{super raster_image_t}
	...
}

{type	jpeg_image_t
	{super raster_image_t}
	...
}

{type vector_image_t
	{super image_t}
	...
}

{type svg_image_t
	{super vector_image_t}
	...
}

При сериализации атрибута scheme_item_t::m_image ObjESSty сохранит в сериализованном представлении реальное имя типа, которому принадлежит объект-изображение. При десериализации ObjESSty сначала извлекает имя типа, ищет специальную фабрику для этого типа и создает объект-изображение. После чего производится десериализация объекта-изображения. Если фабрики для объекта-изображения нет, то десериализация прерывается исключением.

Для того, чтобы иметь возможность при десериализации создавать объекты полиморфных типов, ObjESSty строит для каждого сериализуемого типа специальную фабрику. ObjESSty так же поддерживает список всех фабрик, определенных в приложении. При десериализации атрибута-указателя этот список просматривается в поисках необходимой фабрики. Технически, этот механизм реализуется с помощью типов oess_1::stdsn::factory_registrator_t и oess_1::stdsn::obj_factory_t.

Опциональные атрибуты

Кроме механизма поддержки опциональных атрибутов (см. oess_1.2.0. Опциональные поля) могут быть случаи, когда какой-то параметр (атрибут) для объекта может быть либо определен, либо нет. Например, пусть есть элемент изображения:
class scheme_item_t
  : public oess_1::stdsn::serializable_t
  {
    OESS_SERIALIZER( scheme_item_t )
  private :
    // Надпись под элементом.
    std::string m_title;

    // Параметры шрифта для надписи (шрифт, обычный/курсив/жирный,
    // подчертнутый/перечеркнутый, цвет, угол наклона, ...).
    // Если параметры не заданы, то должен использоваться текущий шрифт.
    // Хранить в этом случае параметры шрифта в объекте не выгодно.
    // Поэтому, если собственные параметры шрифта для объекта не
    // заданы, то этот указатель содержит 0.
    font_params_t * m_font_params;
  ...
  };

{type	scheme_item_t
	{attr m_title {of std::string}}
	{attr m_font_params {of {ptr} font_params_t}}
}

При сериализации атрибутов-указателей ObjESSty проверяет, равен ли указатель нулю. Если равен, то в сериализованном представлении сохраняется признак отсутствия значения атрибута. При десериализации этот признак обрабатывается и атрибуту-указателю присваивается нулевое значение.

Контейнеры указателей

Важной особенностью ObjESSty является то, что ObjESSty поддерживает возможность работы со стандартными STL-контейнерами, которые содержат указатели на сериализуемые объекты. Главным применением для них является хранение списков (vector, list, deque) или карт (map, multimap) полиморфных объектов. Например, некоторое технологическое изображение, состоящее из полиморфных элементов типа scheme_item_t, может быть описано так:
{type	scheme_t
	{attr	m_items
		|| Порядок элементов важен, поэтому используем std::vector.
		{stl-vector}
		|| Элементами вектора могут быть любые классы, производные от
		|| scheme_item_t.
		{of {ptr} scheme_item_t}
	}
	...
}

Опасности использования атрибутов-указателей

ObjESSty не поддерживает циклических связей

С помощью атрибутов-указателей не сложно организовать циклические связи между объектами. Но текущая версия ObjESSty не поддерживает структур с циклическими связями. При попытке сериализовать такую структуру ObjESSty не сможет даже продиагностировать наличие циклической связи. Сериализация ошибки не вызовет, каждый объект в структуре будет сериализован всего один раз. Но, естественно, после десериализации исходная информация о ссылках будет потеряна. Более того, сейчас ObjESSty даже не гарантирует, что атрибуты-указатели после десериализации будут содержать корректные значения и даже, что сама десериализация будет завершена успешно.

ObjESSty преднамерено не поддерживает (пока) работу с циклическими структурами, т.к. наличие циклических ссылок требует двухшаговых операций сериализации и десериализации. Это означает как усложнение алгоритма сериализации/десериализации, так и существенное увеличение времени выполнения данных операций. Кроме того, ситуация усложняется еще и тем, что в ObjESSty существуют механизмы расширяемых типов и неизвестных расширений. Пока не понятно как поступать в случае, если сериализованная на одной стороне циклическая структура не сможет быть полностью десериализованной на другой стороне, т.к. вторая сторона использует более старую схему данных и часть объектов ей просто недоступны, т.к. находятся в неизвестном расширении.

Диагностирование попытки сериализации циклической структуры пока так же затруднена тем, что из-за поддержки множественного и виртуального множественного наследования вполне легальными являются ситуации, когда некоторая базовая составляющая сохраняемого типа задействуется при сериализации/десериализации несколько раз. Например:

{type A ... }
{type B {super {virtual} A } ...}
{type C {super {virtual} C } ...}
{type D {super B} {super C} ...}
При сериализации объекта типа D сериализация атрибутов базовой составляющей D::A будет задействована дважды: один раз через D::B::A, второй раз через D::C::A. Но в образе D должна быть только одна копия атрибутов D::A. Поэтому вспомогательный код ObjESSty контролирует повторную сериализацию/десериализацию одного и того же объекта (в данном случае таким объектом является составляющая D::A) в рамках одной операции. Но при работе с циклическими структурами вспомогательный код ObjESSty не может отличить повторную сериализацию/десериализацию, которая вызвана множественным наследованием от сериализации/десериализации вызванной циклическими ссылками.

ObjESSty создает объекты через new

При десериализации ObjESSty создает объекты динамически, с использованием оператора new. Это означает, что созданные ObjESSty объекты должны быть удалены. Ответственность за это несет прикладной программист. Если динамически созданные ObjESSty объекты удалены не будут, то это приведет к расходу памяти.

Лучше всего всегда хранить в атрибутах-указателях указатели на динамически созданные объекты. Тогда в деструкторе сериализуемого типа можно смело вызывать для них delete.

Потерянные указатели

При десериализации ObjESSty не контролирует текущее значение атрибута-указателя -- ему просто присваивается новое значение. Что может привести к потере указателей и утечке памяти. Для предотвращения этого можно воспользоваться одним из следующих способов:

Несовпадение схем данных

Предположим, что есть приложение 1, у которого в схеме данных определены типы B, C, D, производные от A. Есть так же приложение 2, которое про тип D ничего не знает. Если приложение 1 сериализует через указатель на A объект типа D, то приложение 2 не сможет выполнить десериализацию, т.к. они не знает про тип D.

Если такие случаи возможны, то следует воспользоваться механизмом subclassing by extension: oess_1.2.0. Наследование расширением (subclassing_by_extension).


Документация по ObjESSty. Последние изменения: Fri Oct 13 18:35:37 2006. Создано системой  doxygen 1.4.7
Hosted by uCoz