Делегаты на C++ - [4]
> DelegateList::iterator it;
> for(it = m_DelegateList.begin(); it!= m_DelegateList.end(); ++it) delete (*it);
> m_DelegateList.clear();
> }
> TRet Invoke(PARAMS) {
> DelegateList::const_iterator it;
> for (it = m_DelegateList.begin(); it != --m_DelegateList.end(); ++it) (*it)-›Invoke(ARGS);
> return m_DelegateList.back()-›Invoke(ARGS);
> }
>private:
> DelegateList m_DelegateList;
>};
Вынеся обобщённый таким образом делегат в отдельный файл delegate_impl.h, мы можем сгенерировать его специализацию для любого количества параметров. Например, специализация делегата для пяти параметров получается так:
>// 5 parameters…
>#define SUFFIX 5
>#define TEMPLATE_PARAMS \
>, class TP1, class TP2, class TP3, class TP4, class TP5
>#define TEMPLATE_ARGS , TP1, TP2, TP3, TP4, TP5
>#define PARAMS TP1 p1, TP2 p2, TP3 p3, TP4 p4, TP5 p5
>#define ARGS p1, p2, p3, p4, p5
>#include "delegate_impl.h"
>#undef SUFFIX
>#undef TEMPLATE_PARAMS
>#undef TEMPLATE_ARGS
>#undef PARAMS
>#undef ARGS
Подобные фрагменты для наборов от 0 до 10 параметров можно включить в отдельный файл delegate.h, который и будут подключать пользователи делегатов.
Вот пример использования библиотеки делегатов, которую мы только что получили. Обратите внимание, что он практически полностью соответствует примеру на языке C#, с которого началась эта статья.
>#include ‹iostream›
>#include ‹fstream›
>#include ‹string›
>using namespace std;
>#include "delegate.h"
>class App {
>public:
> // Определяем делегат Callback,
> // который принимает 1 параметр и ничего не возвращает.
> typedef CDelegate1‹void, string› Callback;
> // Это метод класса App.
> void OutputToConsole(string str) { cout ‹‹ str ‹‹ endl; }
> // А это статический метод класса App.
> static void OutputToFile(string str) {
> ofstream fout("output.txt", ios::out | ios::app);
> fout ‹‹ str ‹‹ endl; fout.close();
> }
>};
>int main() {
> App app;
> // Создаём делегат.
> App::Callback callback = NULL;
> if (!callback.IsNull()) callback("1");
> // Добавляем ссылку на OutputToFile.
> // Вызываем её через делегата.
> callback += NewDelegate(App::OutputToFile);
> if (!callback.IsNull()) callback("2");
> // Добавляем ссылку на OutputToConsole.
> // Вызывается вся цепочка:
> // сначала OutputToFile, потом OutputToConsole.
> callback += NewDelegate(&app, &App::OutputToConsole);
> if (!callback.IsNull()) callback("3");
> // Убираем ссылку на OutputToFile.
> // Вызывается только OutputToConsole.
> callback -= NewDelegate(App::OutputToFile);
> if (!callback.IsNull()) callback("4");
> // Убираем оставшуюся ссылку на OutputToConsole.
> callback -= NewDelegate(&app, &App::OutputToConsole);
> if (!callback.IsNull()) callback("5");
>}
Законченный проект Visual Studio 7.0, содержащий этот пример, можно найти на сопровождающем компакт-диске.
Те же и Visual C++ 6.0
На этом можно было бы поставить точку, но остаётся ещё одна нерешённая проблема. Если вы попытаетесь скомпилировать приведённый пример в Visual C++ 6.0, у этого компилятора возникнут проблемы при задании параметра шаблона делегата TRet=void. Дело в том, что в этом случае VC6 не может корректно обработать конструкцию вида:
>virtual TRet Invoke(TP1 p1) {
> // VC6 полагает, что нельзя возвращать выражение типа void.
> return (m_pObj-›*m_pMethod)(p1);
>}
Данная конструкция совершенно законна в соответствии с пунктом 6.6.3/3 Стандарта языка C++. Но VC6 об этом не знает. Поэтому нам придётся искать обходные пути. Чтобы обойти эту недоработку компилятора, необходимо отдельно реализовать все классы CDelegateX для случая TRet=void. Идеальным инструментом для этой цели служит частичная специализация шаблонов, но VC6 не поддерживает и эту возможность языка C++. В результате решение задачи на VC6 превращается в занимательную головоломку.
Первой моей мыслью было воспользоваться техникой, описанной Павлом Кузнецовым в этом же номере журнала в статье "Симуляция частичной специализации". К сожалению, выяснилось, что эта методика неприменима для реализации делегатов на VC6 сразу по двум причинам. Первая причина состоит в том, что использование полиморфизма совместно с навороченными шаблонными конструкциями оказывается "не по зубам" VC6, и он отказывается компилировать классы CStaticDelegateX и CMethodDelegateX, переписанные с использованием частичной специализации. На самом деле, это ещё полбеды, так как эти классы являются внутренней деталью реализации, и применять к ним частичную специализацию не обязательно. Вторая причина носит более фундаментальный характер. Дело в том, что симуляция частичной специализации для класса CDelegate подразумевает создание двух базовых классов (например, CDelegate_void_ для случая TRet=void и CDelegate_ для всех остальных случаев). Затем, в зависимости от значения параметра TRet, класс CDelegate наследовался бы либо от общей, либо от частной реализации. И тут возникает проблема. Дело в том, что в языке C++ операторы не наследуются. Это означает, что operator() нам всё равно придётся реализовывать в классе CDelegate. А мы не сможем реализовать его из-за той самой ошибки VC6, с которой и начался этот раздел. Таким образом, мы заходим в тупик.
Остаётся два пути. Первый путь - написать отдельную реализацию
JavaScript еще никогда не был так прост! Вы узнаете все возможности языка программирования без общих фраз и неясных терминов. Подробные примеры, иллюстрации и схемы будут понятны даже новичку. Легкая подача информации и живой юмор автора превратят нудное заучивание в занимательную практику по написанию кода. Дойдя до последней главы, вы настолько прокачаете свои навыки, что сможете решить практически любую задачу, будь то простое перемещение элементов на странице или даже собственная браузерная игра.
В этой книге автор, сам прошедший путь от разработчика до менеджера в сфере IT, рассказывает неочевидные моменты, которые являются критически важными для правильного управления. Почему разработчики увольняются после повышения зарплаты? Как делать FixedPrice проекты? Почему Scrum не упрощает менеджмент? Книга содержит ответ на эти и многие другие вопросы. В книге есть много баек, которые показывают тяжёлую, но интересную жизнь менеджера в разработке. Иллюстратор обложки: Ксения Ерощенко. Иллюстрации в тексте книги авторские.
Что такое ГЕЙМДИЗАЙН? Это не код, графика или звук. Это не создание персонажей или раскрашивание игрового поля. Геймдизайн – это симулятор мечты, набор правил, благодаря которым игра оживает. Как создать игру, которую полюбят, от которой не смогут оторваться? Знаменитый геймдизайнер Тайнан Сильвестр на примере кейсов из самых популярных игр рассказывает как объединить эмоции и впечатления, игровую механику и мотивацию игроков. Познакомитесь с принципами дизайна, которыми пользуются ведущие студии мира! Создайте игровую механику, вызывающую эмоции и обеспечивающую разнообразие.
Эта книга предназначена для всех, кто желает освоить СУБД MySQL. Для ее чтения вам не нужны никакие специальные знания – достаточно быть пользователем Windows. Вы узнаете, как установить и запустить MySQL, как создать собственную базу данных, как работать с данными при помощи команд SQL, как администрировать базу данных и оптимизировать ее работу. Разработчики веб-приложений на языках PHP, Perl и Java найдут в этой книге полезные сведения по использованию базы данных MySQL в соответствующих приложениях.
РАССЫЛКА ЯВЛЯЕТСЯ ЧАСТЬЮ ПРОЕКТА RSDN, НА САЙТЕ КОТОРОГО ВСЕГДА МОЖНО НАЙТИ ВСЮ НЕОБХОДИМУЮ РАЗРАБОТЧИКУ ИНФОРМАЦИЮ, СТАТЬИ, ФОРУМЫ, РЕСУРСЫ, ПОЛНЫЙ АРХИВ ПРЕДЫДУЩИХ ВЫПУСКОВ РАССЫЛКИ И МНОГОЕ ДРУГОЕ.