oess_1.4.0. Базовый каркас для умных указателей

Немного истории

Первоначально в ObjESSty появился класс oess_1::stdsn::shptr_t для решения проблемы умных указателей. Подробнее о причинах его появления можно прочитать в Необходимость класса oess_1::stdsn::shptr_t. Со временем возможностей базовой реализации oess_1::stdsn::shptr_t стало не хватать.

Во-первых, в некоторых случаях для защиты умных указателей в многопоточных приложениях использовался не механизм подсчета ссылок, а механизм клонирования объектов, на которые указывали умные указатели. Для этого класс oess_1::stdsn::shptr_t был расширен несколькими методами (oess_shptr_assign, oess_shptr_reset) с тем, чтобы их можно было переопределять в производных классах.

Во-вторых, в v.1.2.0 появился механизм oess_1.2.0. Наследование расширением (subclassing_by_extension). Для работы с объектами, которые поддерживают этот механизм так же требовалось использовать атрибуты-указатели. И для облегчения работы с ними так же требовался класс умного указателя. Но применить для этих целей oess_1::stdsn::shptr_t было невозможно. Дело в том, что oess_1::stdsn::shptr_t представлялся в DDL как:

{type oess_1::stdsn::shptr_t
  {attr m_ptr {of {ptr} oess_1::stdsn::serializable_t} }
}
Т.е. shptr_t позволял использовать одинаковое DDL-представление для всех сериализуемых типов. Но для типов с subclassing by extension умный указатель в DDL должен был выглядеть так:
{type some_smart_pointer_t
	{attr	m_ptr {of {extension_of} some_type_t}}
}
Причем в качестве some_type_t нельзя было указывать имя oess_1::stdsn::serializable_t, т.к. он не входит в иерархию subclassing by extension.

Для того, чтобы поддержать умные указатели для механизма subclassing by extension потребовалось бы создать новый тип умных указателей. Но зачем же иметь в ObjESSty несколько разных классов с одинаковой функциональностью и предназначенных для одинаковых целей? Вместо этого был создан каркас для умных указателей, oess_1::stdsn::shptr_skeleton_t, который можно использовать для поддержки механизма subclassing by extension. А так же класс oess_1::stdsn::shptr_t сделан наследником oess_1::stdsn::shptr_skeleton_t для того, чтобы унаследовать всю имеющуюся функциональность.

Класс shptr_skeleton_t

Класс oess_1::stdsn::shptr_skeleton_t является абстрактным классом, который должен быть непосредственным базовым классом для конкретных классов умных указателей. Т.е. класс умного указателя должен быть произведен не от oess_1::stdsn::serializable_t, а от oess_1::stdsn::shptr_skeleton_t.

Класс oess_1::stdsn::shptr_skeleton_t содержит функциональность по управлению хранимым в нем указателем, но не имеет собственного DDL описания. Атрибут oess_1::stdsn::shptr_skeleton_t::m_ptr (указатель на контролируемый объект) должен быть объявлен в DDL описании производного от shptr_skeleton_t класса.

Класс shptr_skeleton_t поддерживает разные политики владения указателем. Т.е. он может использоваться как для реализации владения на основе подсчета ссылок, на основе клонирования объекта, на основе реализованной пользователем политики. Для этого одним из параметров шаблона oess_1::stdsn::shptr_skeleton_t является класс Own_policy. Класс shptr_skeleton_t владеет атрибутом m_own_policy, который и является реальной политикой, управляющей временем жизни контролируемого объекта. Меняя при инстанциировании shptr_skeleton_t типы политик можно достигать разного поведения shptr_skeleton_t. Подробнее о политиках владения см. Политики владения.

Использование shptr_skeleton_t для создания собственного умного указателя

Для того, чтобы создать собственный умный указатель на основе shptr_skeleton_t нужно сделать две вещи:

1. Описать на DDL схему данных умного указателя. Обязательно указать в ней атрибут m_ptr соотвествующего типа. Например, для простых умных указателей:

{type	my_smart_ptr_t
	{attr m_ptr {of {ptr} my_class_t}}
}
или для механизма subclassing by extension:
{type	my_smart_ptr_t
	{attr m_ptr {of {extension_of} my_class_t}}
}

Важно здесь вот что:

2. Описать на C++ свой класс умного указателя следующим образом:

class my_smart_ptr_t
  : public oess_1::stdsn::shptr_skeleton_t< my_class_t >
  {
    // Может так же использоваться макрос OESS_SERIALIZER_EX().
    OESS_SERIALIZER( my_smart_ptr_t )
    OESS_1_SHPTR_IFACE( my_smart_ptr_t, my_class_t,
        oess_1::stdsn::shptr_skeleton_t< my_class_t > )
  };
Причем при описании класса на C++ не важно, поддерживает ли он обычные указатели или указатели для механизма subclassing by extension.

Политики владения

Политика владения -- это объект, который отвечает за уничтожение контролируемого умным указателем объекта в нужный момент времени. Политика задается классу shptr_skeleton_t параметром шаблона. Класс политики владения должен удовлетворять интерфейсу:
template< class T >
class policy_t
  {
  public :
    policy_t( T * & pointer_holder );

    policy_t(
      T * & pointer_holder, T * pointer );

    ~policy_t();

    void
    reassign();

    void
    link(
      const policy_t< T > & owner_policy );

    std::auto_ptr< T >
    release();
  };

Фокус с объектами-политиками состоит в том, что им класс shptr_skeleton_t передает ссылку на свой атрибут m_ptr. И объекты-политики должны корректно устанавливать или обнулять его значение. Практически во всех случаях именно объект-политика устанавливает значение атрибута m_ptr (в своих конструкторах, методах link и release). Единственным исключением является метод reassign, но о нем речь зайдет ниже.

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

Второй конструктор необходим, когда в конструктор shptr_skeleton_t передается объект, которым никто не владеет. Единственным владельцем должен стать данный объект-политика.

Метод link вызывается в операторе копирования класса shptr_skeleton_t. Его атрибутом является объект-политика из объекта shptr_skeleton_t в правой части присваивания.

Метод release() вызывается либо из метода oess_1::stdsn::shptr_skeleton_t::destroy() (когда пользователь хочет отказаться от владения объектом), либо из метода oess_1::stdsn::shptr_skeleton_t::oess_pre_unpack() (когда текущий контролируемый объект становится неактуальным, т.к. после десериализации контролируемым должен стать другой объект). Должен вовращаться std::auto_ptr, в котором находится указатель на ранее контролировавшийся объект, если его больше никто не контролирует (т.е. объект будет уничтожен std::auto_ptr). Либо в std::auto_ptr должен быть нулевой указатель (т.е. объект продолжает контролировать кто-то еще и объект не может быть физически уничтожен).

Отдельная ситуация с методом reassign(). В большинстве случаев именно объект-политика управляет значением атрибута m_ptr класса shptr_skeleton. Но есть одно исключение -- при десериализации объекта значение атрибута m_ptr устанавливается самой ObjESSty. Поэтому в методе oess_1::stdsn::shptr_skeleton_t::oess_post_unpack() вызывается метод reassign() чтобы объект-политика могла создать всю необходимую инфраструктуру для котроля за новым объектом.

Политика на основе счетчика ссылок

Политика на основе подсчета количества ссылок на объект реализуется классом oess_1::stdsn::refcountable_policy_t. Это политика, которая используется классом shptr_skeleton_t по умолчанию.

Политика на основе клонирования

Политика на основе клонирования реализуется классом oess_1::stdsn::cloneable_policy_t. Принцип ее работы прост: когда требуется создать копию умного указателя, для новой копии создается новая копия контролируемого объекта (путем клонирования).

Для того, чтобы тип мог использоваться совместно с политикой cloneable_policy_t необходимо, чтобы он предоставлял метод:

T *
clone() const;
который бы возвращал указатель на новый динамически созданный объект.

Либо, метод clone() может иметь следующий прототип:

std::auto_ptr< T >
clone() const;

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

Изменения в shptr

В версии 1.4.0 класс oess_1::stdsn::shptr_t был изменен: он сделал производным от oess_1::stdsn::shptr_skeleton_t (с политикой на основе подсчета ссылок) и из него удалены все методы за исключением конструкторов и оператора копирования. Это означает, что весь код, который оработат на основе старых методов oess_shptr_assign, oess_shptr_reset при переходе на 1.4.0 должен быть переписан.

Для остальных клиентов класса shptr_t будет видно только одно изменение: методы get() теперь порождают исключения, если приведение типов невозможно. В предыдущих версиях shptr_t в таких случаях возвращался нулевой указатель.


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