template‹class TRet, class TP1›
class CStaticDelegate1: public IDelegate1‹TRet, TP1› {
public:
typedef TRet (*PFunc)(TP1);
CStaticDelegate1(PFunc pFunc) {
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: