Быстрые делегаты C++ - Заглушка менеджера клонирования объектов
ОГЛАВЛЕНИЕ
Заглушка менеджера клонирования объектов
В предыдущей версии только указатель (ссылка) на связанный объект мог быть сохранен в делегате для указателя функции-члена (связывание по аргументу), чтобы ее можно было вызвать, когда делегат будет вызван позже. Мы решили добавить поддержку клонирования связанного объекта в делегате, чтобы указатель функции-члена мог быть вызван из внутренней копии связанного объекта, а не по указателю на связанный объект.
Только когда информация о типе указателя функции-члена или связанного с ним объекта будет доступна, тогда указатель функции-члена будет связан с делегатом. Поэтому необходимо сохранение типа заглушки для клонирования и разрушения объекта. Аналогичная статическая функция-член вложенного шаблонного класса, как показано выше, используемая для вызова указателя функции-члена, может быть снова использована здесь.
typedef void * (*obj_clone_man_type)(delegate &, void const *);
obj_clone_man_type obj_clone_man_ptr_;
template < typename T >
struct obj_clone_man_t
{
inline static void * typed_obj_manager_(delegate & dg, void const * untyped_obj_src)
{
T * typed_obj_src =const_cast < T * >
(static_cast < T const * > (untyped_obj_src)); typed_obj_src;
if(dg.obj_ptr_)
{
T * typed_obj_this = static_cast < T * > (dg.obj_ptr_);
delete typed_obj_this;
dg.obj_ptr_ = 0;
}
if(0 != typed_obj_src)
{
T * obj_new = new T(*typed_obj_src);
dg.obj_ptr_ = obj_new;
}
T * typed_obj = static_cast < T * > (dg.obj_ptr_); typed_obj;
return typed_obj;
}
}; // шаблон<имя-типа T> struct obj_clone_man_t
obj_clone_man_ptr_ = &obj_clone_man_t<T>::typed_obj_manager_;
Объект может быть успешно клонирован или разрушен путем использования obj_clone_man_ptr_, как показано ниже:
delegate dg1, dg2;
// копирует клонированный внутри объект dg2 в dg1
(*dg1.obj_clone_man_ptr_)(dg1, dg2.obj_ptr_);
// разрушает клонированный внутри объект dg1
(*dg1.obj_clone_man_ptr_)(dg1, 0);
Размер связанного объекта неизвестен, и он может иметь размер от нескольких байт до нескольких сотен байт, или даже больше. Поэтому выделение/освобождение памяти кучи неизбежно. Это противоречит требованию создания быстрых делегатов, так как главной целью использования быстрых делегатов было избегание использования памяти кучи любой ценой. (Пользовательский распределитель памяти, который будет описан позже, может помочь смягчить эту проблему.)
Мы решили включить функцию клонирования в наш делегат для поддержки интеллектуальных указателей. В отличие от C#, в C++ нет встроенного «сборщика мусора», но у нас есть интеллектуальные указатели. Чтобы работать с интеллектуальными указателями, нужно иметь возможность копировать или разрушать экземпляр интеллектуального указателя безопасным для типа способом (другими словами, должен вызываться надлежащий оператор присваивания или деструктор интеллектуального указателя). Для этой цели у нас уже есть функция-заглушка. Но есть еще два необходимых условия, которые нужно выполнить. (Идея заимствована из boost::mem_fn.)
- Функция get_pointer(), которая принимает ссылку или постоянную ссылку на интеллектуальный указатель и возвращает указатель на сохраненный целевой объект, должна находиться в подходящем пространстве имен (включая зависящую от аргументов проверку).
- Класс интеллектуального указателя должен предоставлять открытый интерфейс (typedef) element_type. (std::auto_ptr<T>, boost::shared_ptr, и его «брат или сестра», loki::smartPtr предоставляет этот публичный интерфейс.)
Следующие две версии get_pointer() реализованы в нашем делегате по умолчанию:
namespace fd
{
template<class T> inline
T * get_pointer(T * p)
{
return p;
}
template<class T> inline
T * get_pointer(std::auto_ptr<T> & p)
{
return p.get();
}
} // пространство имен fd
boost::shared_ptr определяет get_pointer() для себя в пространстве имен boost. Те компиляторы, которые реализуют проверку Кениг (зависимая от аргументов проверка), такие как VC71 или выше, GCC3.x.x.x, смогут увидеть определение, так что наш делегат может распознать и поддержать его без добавления каких-либо дополнительных строк кода. Но для тех компиляторов, которые не реализуют преобразование (проверку) аргументов должным образом или не имеют ее вовсе, мы можем создать соответствующий get_pointer() в пространстве имен fd.
#if defined(_MSC_VER) && _MSC_VER < 1300
// Даже если get_pointer определен в boost/shared_ptr.hpp, VC6 не
// реализует проверку Кениг (зависимую от аргументов проверку), то есть не может найти определение,
// Поэтому мы определяем get_pointer в явном виде в пространстве имен fd, чтобы решить проблему с плохим компилятором
namespace fd
{
template<class T>
T * get_pointer(boost::shared_ptr<T> const & p)
{
return p.get();
}
}
#endif // #если определен(_MSC_VER) && _MSC_VER < 1300