Делегаты на C++ - [3]

Шрифт
Интервал

>m_pFunc = pFunc; }

> virtual TRet Invoke(TP1 p1) { return m_pFunc(p1); }

> virtual bool Compare(IDelegate1‹TRet, TP1›* pDelegate) {

>  CStaticDelegate1‹TRet, TP1›* pStaticDel = dynamic_cast‹CStaticDelegate1‹TRet, TP1›* ›(pDelegate);

>  if (pStaticDel == NULL || pStaticDel-›m_pFunc != m_pFunc) return false;

>  return true;

> }

>private:

> PFunc m_pFunc;

>};


Как видим, мы вынуждены постоянно "таскать" за собой список параметров шаблона ‹TRet, TP1›. Для делегата с 10-ю параметрами эти списки полностью загромоздят код. Кроме того, вручную дублировать практически идентичные классы 11 раз - не самая удачная идея. Чтобы избежать лишнего дублирования кода, прибегнем к самому сильнодействующему (и самому опасному) средству языка C++ - препроцессору. Идея состоит в том, чтобы обозначить различающиеся участки кода в реализации классов CDelegateX макросами. Эти различающиеся участки можно разделить на 4 типа:

• Параметры шаблонов (например, ‹…, class TP1, class TP2, class TP3›). Список параметров шаблона обозначим макросом TEMPLATE_PARAMS.

• Аргументы шаблонов (например, ‹…, TP1, TP2, TP3›). Список аргументов шаблона обозначим макросом TEMPLATE_ARGS.

• Параметры функции Invoke (например, (TP1 p1, TP2 p2, TP3 p3)). Список этих параметров обозначим макросом PARAMS.

• Аргументы функции Invoke (например, (p1, p2, p3)). Список этих аргументов обозначим макросом ARGS.

Кроме этих макросов, нам потребуется макрос SUFFIX, который принимает значения от 0 до 10 и дописывается к именам классов следующим образом:

>#define COMBINE(a,b) COMBINE1(a,b)

>#define COMBINE1(a,b) a##b


>#define I_DELEGATE COMBINE(IDelegate, SUFFIX)

>#define C_STATIC_DELEGATE COMBINE(CStaticDelegate, SUFFIX)

>#define C_METHOD_DELEGATE COMBINE(CMethodDelegate, SUFFIX)

>#define C_DELEGATE COMBINE(CDelegate, SUFFIX)


ПРИМЕЧАНИЕ Обратите внимание на использование вспомогательного макроса COMBINE1. Если напрямую реализовать макрос COMBINE как #define COMBINE(a,b) a##b, то результатом подстановки COMBINE(IDelegate, SUFFIX) будет "IDelegateSUFFIX". А это совсем не то, что мы хотим получить. Поэтому использование COMBINE1 в данном случае необходимо.

Окончательная версия делегата, обобщённая с помощью всех этих макросов, будет выглядеть так:

>template‹class TRet TEMPLATE_PARAMS›

>class I_DELEGATE {

>public:

> virtual ~I_DELEGATE() {}

> virtual TRet Invoke(PARAMS) = 0;

> virtual bool Compare(I_DELEGATE‹TRet TEMPLATE_ARGS›* pDelegate) = 0;

>};


>template‹class TRet TEMPLATE_PARAMS›

>class C_STATIC_DELEGATE: public I_DELEGATE‹TRet TEMPLATE_ARGS› {

>public:

> typedef TRet (*PFunc)(PARAMS);

> C_STATIC_DELEGATE(PFunc pFunc) { m_pFunc = pFunc; }

> virtual TRet Invoke(PARAMS) { return m_pFunc(ARGS); }

> virtual bool Compare(I_DELEGATE‹TRet TEMPLATE_ARGS›* pDelegate) {

>  C_STATIC_DELEGATE‹TRet TEMPLATE_ARGS›* pStaticDel = dynamic_cast‹C_STATIC_DELEGATE‹TRet TEMPLATE_ARGS›*›(pDelegate);

>  if (pStaticDel == NULL || pStaticDel-›m_pFunc != m_pFunc) return false;

>  return true;

> }

>private:

> PFunc m_pFunc;

>};


>template‹class TObj, class TRet TEMPLATE_PARAMS›

>class C_METHOD_DELEGATE: public I_DELEGATE‹TRet TEMPLATE_ARGS› {

>public:

> typedef TRet (TObj::*PMethod)(PARAMS);

> C_METHOD_DELEGATE(TObj* pObj, PMethod pMethod) {

>  m_pObj = pObj;

>  m_pMethod = pMethod;

> }

> virtual TRet Invoke(PARAMS) { return (m_pObj-›*m_pMethod)(ARGS); }

> virtual bool Compare(I_DELEGATE‹TRet TEMPLATE_ARGS›* pDelegate) {

>  C_METHOD_DELEGATE‹TObj, TRet TEMPLATE_ARGS›* pMethodDel = dynamic_cast‹C_METHOD_DELEGATE‹TObj, TRet TEMPLATE_ARGS›*›(pDelegate);

>  if (pMethodDel == NULL || pMethodDel-›m_pObj != m_pObj || pMethodDel-›m_pMethod != m_pMethod) { return false; }

>  return true;

> }

>private:

> TObj *m_pObj;

> PMethod m_pMethod;

>};


>template‹class TRet TEMPLATE_PARAMS›

>I_DELEGATE‹TRet TEMPLATE_ARGS›* NewDelegate(TRet (*pFunc)(PARAMS)) {

> return new C_STATIC_DELEGATE‹TRet TEMPLATE_ARGS›(pFunc);

>}


>template ‹class TObj, class TRet TEMPLATE_PARAMS›

>I_DELEGATE‹TRet TEMPLATE_ARGS›* NewDelegate(TObj* pObj, TRet (TObj::*pMethod)(PARAMS)) {

> return new C_METHOD_DELEGATE‹TObj, TRet TEMPLATE_ARGS› (pObj, pMethod);

>}


>template‹class TRet TEMPLATE_PARAMS›

>class C_DELEGATE {

>public:

> typedef I_DELEGATE‹TRet TEMPLATE_ARGS› IDelegate;

> typedef std::list‹IDelegate*› DelegateList;

> C_DELEGATE(IDelegate* pDelegate = NULL) { >Add(pDelegate); }

> ~C_DELEGATE() { RemoveAll(); }

> bool IsNull() { return (m_DelegateList.empty()); }

> C_DELEGATE‹TRet TEMPLATE_ARGS›& operator=(IDelegate* pDelegate) {

>  RemoveAll();

>  Add(pDelegate);

>  return *this;

> }

> C_DELEGATE‹TRet TEMPLATE_ARGS›& operator+=(IDelegate* pDelegate) {

>  Add(pDelegate);

>  return *this;

> }

> C_DELEGATE‹TRet TEMPLATE_ARGS›& operator-=(IDelegate* pDelegate) {

>  Remove(pDelegate);

>  return *this;

> }

> TRet operator()(PARAMS) {

>  return Invoke(ARGS);

> }

>private:

> void Add(IDelegate* pDelegate) {

>  if(pDelegate != NULL) m_DelegateList.push_back(pDelegate);

> }

> void Remove(IDelegate* pDelegate) {

>  DelegateList::iterator it;

>  for(it = m_DelegateList.begin(); it!= m_DelegateList.end(); ++it) {

>   if((*it)-›Compare(pDelegate)) {

>    delete (*it);

>    m_DelegateList.erase(it);

>    break;

>   }

>  }

> }

> void RemoveAll() {


Рекомендуем почитать
JavaScript с нуля

JavaScript еще никогда не был так прост! Вы узнаете все возможности языка программирования без общих фраз и неясных терминов. Подробные примеры, иллюстрации и схемы будут понятны даже новичку. Легкая подача информации и живой юмор автора превратят нудное заучивание в занимательную практику по написанию кода. Дойдя до последней главы, вы настолько прокачаете свои навыки, что сможете решить практически любую задачу, будь то простое перемещение элементов на странице или даже собственная браузерная игра.


Как хорошему разработчику не стать плохим менеджером

В этой книге автор, сам прошедший путь от разработчика до менеджера в сфере IT, рассказывает неочевидные моменты, которые являются критически важными для правильного управления. Почему разработчики увольняются после повышения зарплаты? Как делать FixedPrice проекты? Почему Scrum не упрощает менеджмент? Книга содержит ответ на эти и многие другие вопросы. В книге есть много баек, которые показывают тяжёлую, но интересную жизнь менеджера в разработке. Иллюстратор обложки: Ксения Ерощенко. Иллюстрации в тексте книги авторские.


Геймдизайн. Рецепты успеха лучших компьютерных игр от Super Mario и Doom до Assassin’s Creed и дальше

Что такое ГЕЙМДИЗАЙН? Это не код, графика или звук. Это не создание персонажей или раскрашивание игрового поля. Геймдизайн – это симулятор мечты, набор правил, благодаря которым игра оживает. Как создать игру, которую полюбят, от которой не смогут оторваться? Знаменитый геймдизайнер Тайнан Сильвестр на примере кейсов из самых популярных игр рассказывает как объединить эмоции и впечатления, игровую механику и мотивацию игроков. Познакомитесь с принципами дизайна, которыми пользуются ведущие студии мира! Создайте игровую механику, вызывающую эмоции и обеспечивающую разнообразие.



MySQL 5.0. Библиотека программиста

Эта книга предназначена для всех, кто желает освоить СУБД MySQL. Для ее чтения вам не нужны никакие специальные знания – достаточно быть пользователем Windows. Вы узнаете, как установить и запустить MySQL, как создать собственную базу данных, как работать с данными при помощи команд SQL, как администрировать базу данных и оптимизировать ее работу. Разработчики веб-приложений на языках PHP, Perl и Java найдут в этой книге полезные сведения по использованию базы данных MySQL в соответствующих приложениях.


Программирование на Visual C++. Архив рассылки

РАССЫЛКА ЯВЛЯЕТСЯ ЧАСТЬЮ ПРОЕКТА RSDN, НА САЙТЕ КОТОРОГО ВСЕГДА МОЖНО НАЙТИ ВСЮ НЕОБХОДИМУЮ РАЗРАБОТЧИКУ ИНФОРМАЦИЮ, СТАТЬИ, ФОРУМЫ, РЕСУРСЫ, ПОЛНЫЙ АРХИВ ПРЕДЫДУЩИХ ВЫПУСКОВ РАССЫЛКИ И МНОГОЕ ДРУГОЕ.