Написание приложений Win32 Apps с помощью одних классов C++ (часть 1) - Связанные действия

ОГЛАВЛЕНИЕ

Связанные действия

Используя XRefChain в качестве параметра WrpBase, можно связать вместе "обертки для одной и той же сущности" и идти по ним.

С этим «хождением» можно связать действия. Например, обеспечить возможность отправлять сообщения Windows оберткам HWND, но не только.

EChainedAction<W, I, A> является наследуемым классом (W – его производный класс), связывающим действие с параметром A с проходом по итератору I, который должен быть совместимым с STL поступательным итератором, разыменовывающимся в W* (чей I::operator*() возвращает W*). Такой итератор можно получить посредством WrpBase::get_range(...).

Функция Act(const A& a, iterator first, iterator last) выполняет итерации с начала до конца, вызывая функцию-член OnAction(const A& a), предположительно переопределенную в W.

Однако итерации выполняются не путем чистого прямого цикла, а путем рекурсии по хранимым параметрам: Act заполняет структуру данных в стеке и сохраняет ее адрес в сохраненную в каждом потоке статическую переменную, храня предыдущий адрес (переменная из потока – переменная, к которой обращаются посредством карты, ключом которой является идентификатор текущего потока), затем вызывает Default(). Default извлекает структуру данных и выполняет итерации, вызывая OnAction. Если OnAction возвращает true, Default сразу возвращает true, иначе выполняет итерацию для следующего элемента, пока не достигнут конец. В этой точке возвращает false.

Следовательно, в переопределении OnAction можно:
•    Просто вернуть false. Итерация продолжится для следующего связанного элемента.
•    Просто вернуть true. Итерация остановится.
•    Вызвать OnAction при необходимости. Предполагается итерация для следующих элементов, и управление возвращается вам.

Это примерно похоже на создание другого производного класса от оконной процедуры и вызов предыдущей процедуры при необходимости произвольное число раз.

События

События являются сочетанием интеллектуальных указателей и связанных действий. Здесь они реализованы в виде «функторов», объявляемых как переменные-члены класса (источник события), прикрепляемые посредством внутренних объектов («приемников»), отправляющих действие зарегистрированным "получателям".

Event<A> является struct, немного превосходящей член operator()(const A&), предполагающий, что событие надлежит обернуть связываемыми обертками. Оператор получает цепь и вызывает Act.

EventRcv<D> - базовый класс для обобщенного производного класса (D), предоставляющий RegisterEvent<A>(Event<A>&,bool(D::*pmemfun)(consty A&) ) и UnregisterEvent<A>(Event<A>&). Первая функция создает XTypedSync<A,R>, связывающий событие с указанной функцией-членом. Вторая функция удаляет связь.

XTypedSync<A,R> порождается от XSync<A>, являющегося связываемой оберткой для Event, имеющей параметр A. XSync реализует OnAction, делегирующий виртуальной функции DoAction, реализованной в производном XTypedSync, вызывающем указанную функцию-член (посредством указателя на член, сохраненного во время привязки). Необходимо использовать виртуальную функцию, потому что разные XTypedSync (ссылающиеся на разных получателей, следовательно, имеющих разные параметры R) должны связывать все вместе, как XSync<A>.

Итак, вызов события будет вызывать зарегистрированную функцию-член для всех связанных получателей.

Регистрация WNDCLASS

Если вы считаете заполнение поля такой структуры данных, как WNDCLASSEX, очень неприятной задачей, то эта обертка для вас. SWndClassExReg регистрирует класс окна, разрегистрируя его, когда больше нет ссылок.

Он порождается от WrpBase, кастомизируя Destroy.

Конструкторы пытаются заполнить WNDCLASSEX переданными параметрами:
•    Версия загружает из ресурсов с переданным идентификатором (иконка и курсор), второй конструктор использует переданные необработанные значения.
•    Оба конструктора регистрируют класс окна и сохраняют возвращенный ATOM.
•    Функция Destroy обертки (она вызывается, когда отцепляется последняя обертка-владелец) вызывает UnregisterWndClass, передавая сохраненный ATOM.

Цикл обработки сообщений (SMsgLoop)

Цикл обработки сообщений реализуется SMsgLoop. Можно создать столько его экземпляров, сколько нужно. Как правило, один будет в WinMain, тогда как остальные будут там, где нужны "модальные циклы" (например: при запросе координат мыши, чтобы определить, куда поместить объект).

Сам цикл реализуется функцией LoopBody (которая сама не является циклом), реализующей извлечение и отправку сообщений вместе с фильтрацией и переводом сообщений и обработкой во время простоя. Эта функция вызывается LoopWhile, выполняющим цикл, пока переданная ссылка bool не станет false или пока не будет получено WM_QUIT. LoopWhile вызывается Loop, предоставляющим «всегда истинное» значение для LoopWhile.

Такое "разделение цикла" на три составляющих позволяет использовать SMessageLoop для выполнения модального цикла внутри приложения (с использованием LoopWhile) или для отправки сообщения во время расчета цикла (вызывая LoopBody из главного цикла).

SMessageLoop также предоставляет член для хранения HWND (который может быть главным окном приложения), уничтожение которого вызовет отправку WM_QUIT.

Если SMsgLoop создается с параметром bAutoQuit, установленным в true, первое окно, созданное посредством указанного цикла обработки сообщений, становится главным окном цикла обработки сообщений.