Чтение онлайн

на главную

Жанры

Делегаты на C++

Шаргин Александр

Шрифт:

В первую очередь нам нужен инструмент для преобразования типов, который на этапе компиляции превращал бы void в int, а остальные типы оставлял бы без изменений. В C++ такие преобразования типов осуществляются с использованием полной специализации шаблонов (к счастью, её VC6 поддерживает). В нашем случае реализация будет выглядеть так.

template‹class T›

struct DelegateRetVal {

 typedef T Type;

};

template‹›

struct DelegateRetVal‹void› {

 typedef int Type;

};

Как

видим, внутри класса DelegateRetVal определяется тип Type, который в общем случае совпадает с параметром шаблона T. Для случая T=void это поведение переопределяется с использованием специализации: в этом случае тип Type определяется как int. В результате, выражение DelegateRetVal‹TRet›::Type будет на этапе компиляции принимать нужный нам тип при любых значениях TRet.Следующий шаг - модификация классов CStaticDelegateX и CMethodDelegateX. Во-первых, нужно заменить значение, возвращаемое методом Invoke, на DelegateRetVal‹TRet›::Type. Во-вторых, нужно реализовать два дополнительных класса, CStaticDelegateVoidX и CMethodDelegateVoidX, для обработки случая TRet=void. Единственным их отличием от одноимённых классов без суффикса "Void" будет другая реализация метода Invoke:

#define C_STATIC_DELEGATE_VOID COMBINE(CStaticDelegateVoid, SUFFIX)

#define C_METHOD_DELEGATE_VOID COMBINE(CMethodDelegateVoid, SUFFIX)

template‹class TRet TEMPLATE_PARAMS›

class C_STATIC_DELEGATE_VOID: public I_DELEGATE‹TRet TEMPLATE_ARGS› {

 …

 virtual DelegateRetVal‹TRet›::Type Invoke(PARAMS) {

m_pFunc(ARGS);

return 0;

 }

 …

};

template‹class TObj, class TRet TEMPLATE_PARAMS›

class C_METHOD_DELEGATE_VOID: public I_DELEGATE‹TRet TEMPLATE_ARGS› {

 …

 virtual DelegateRetVal‹TRet›::Type Invoke(PARAMS) {

(m_pObj-›*m_pMethod)(ARGS);

return 0;}

 …

};

ПРИМЕЧАНИЕ В этом месте может возникнуть соблазн избежать дублирования кода, породив класс CStaticDelegateVoidX от CStaticDelegateX и CMethodDelegateVoidX от CMethodDelegateX соответственно. К сожалению, это не будет работать. Хотя мы и переопределяем виртуальный метод Invoke в производных классах, теоретическая возможность обратиться к Invoke базовых классов сохраняется. Поэтому компилятор честно попытается сгенерировать их реализацию. А это в случае TRet=void в очередной раз приведёт к ошибке, которую мы пытаемся обойти. Поэтому дублирование кода в данном случае неизбежно.

Осталось сделать последний шаг - перегрузить функцию NewDelegate ещё двумя реализациями:

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 TRet TEMPLATE_PARAMS›

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

 return new C_STATIC_DELEGATE_VOID‹void TEMPLATE_ARGS›(pFunc);

}

 // Аналогично для CMethodDelegate*

В этом месте нас поджидает ещё один сюрприз. В большинстве случаев этот код будет работать, как по маслу. Но при задании TRet=void возникнет неоднозначность при обращении к функции NewDelegate. Правила разрешения перегрузки шаблонов функций описаны в разделе 14.5.5.2 Стандарта языка C++. В соответствии с этими правилами вторая версия NewDelegate не считается более специализированной, чем первая, так как для вызова обоих вариантов функции не требуется неявных преобразований типа.

Чтобы разрешить эту неоднозначность, придётся ввести дополнительный параметр функции NewDelegate, по которому и будет выбираться нужная версия функции:

// Параметр этого типа будет индикатором

template‹int use›

class UseVoid {};

template‹class TRet TEMPLATE_PARAMS›

I_DELEGATE‹TRet TEMPLATE_ARGS›* NewDelegate(TRet (*pFunc)(PARAMS), UseVoid‹0›) {

 return new C_STATIC_DELEGATE‹TRet TEMPLATE_ARGS›(pFunc);

}

template‹class TRet TEMPLATE_PARAMS›

I_DELEGATE‹TRet TEMPLATE_ARGS›* NewDelegate(TRet (*pFunc)(PARAMS), UseVoid‹1›) {

 return new C_STATIC_DELEGATE_VOID‹TRet TEMPLATE_ARGS›(pFunc);

}

Тем самым мы избавляемся от неоднозначности. Но возникает другая проблема. Теперь при вызове NewDelegate необходимо явно указывать, какая версия функции нам нужна:

void f;

int g;

NewDelegate(f, UseVoid‹1›);

NewDelegate(g, UseVoid‹0›);

Чтобы избавиться от необходимости явно указывать параметр UseVoid, напишем третий вариант функции NewDelegate, который будет автоматически (причём на этапе компиляции) определять и вызывать нужную версию этой функции. Для реализации этой идеи нам потребуется механизм преобразования типа TRet в константу 1 (в случае TRet=void) или 0 (для всех остальных типов). Мы уже решали аналогичную задачу в классе DelegateRetVal, поэтому теперь решение записывается без труда:

template‹class T›

struct IsVoid {

 enum {
 Result = 0};

};

template‹› struct

IsVoid‹void› {

 enum {Result = 1};

};

Теперь воспользуемся классом IsVoid для выбора нужного варианта функции NewDelegate.

template‹class TRet TEMPLATE_PARAMS›

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

Поделиться:
Популярные книги

Вперед в прошлое 3

Ратманов Денис
3. Вперёд в прошлое
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Вперед в прошлое 3

Никто и звать никак

Ром Полина
Фантастика:
фэнтези
7.18
рейтинг книги
Никто и звать никак

Мятежник

Прокофьев Роман Юрьевич
4. Стеллар
Фантастика:
боевая фантастика
7.39
рейтинг книги
Мятежник

Пропала, или Как влюбить в себя жену

Юнина Наталья
2. Исцели меня
Любовные романы:
современные любовные романы
6.70
рейтинг книги
Пропала, или Как влюбить в себя жену

Темный Патриарх Светлого Рода 6

Лисицин Евгений
6. Темный Патриарх Светлого Рода
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Темный Патриарх Светлого Рода 6

Случайная мама

Ручей Наталья
4. Случайный
Любовные романы:
современные любовные романы
6.78
рейтинг книги
Случайная мама

На границе империй. Том 8

INDIGO
12. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 8

Сердце Дракона. Том 19. Часть 1

Клеванский Кирилл Сергеевич
19. Сердце дракона
Фантастика:
фэнтези
героическая фантастика
боевая фантастика
7.52
рейтинг книги
Сердце Дракона. Том 19. Часть 1

Кодекс Охотника. Книга V

Винокуров Юрий
5. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
4.50
рейтинг книги
Кодекс Охотника. Книга V

Дракон - не подарок

Суббота Светлана
2. Королевская академия Драко
Фантастика:
фэнтези
6.74
рейтинг книги
Дракон - не подарок

Беглец

Кораблев Родион
15. Другая сторона
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Беглец

Хозяйка старой усадьбы

Скор Элен
Любовные романы:
любовно-фантастические романы
8.07
рейтинг книги
Хозяйка старой усадьбы

Развод и девичья фамилия

Зика Натаэль
Любовные романы:
современные любовные романы
5.25
рейтинг книги
Развод и девичья фамилия

Я еще не князь. Книга XIV

Дрейк Сириус
14. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я еще не князь. Книга XIV