Для удобного решения некоторых классов задач необходимо иметь возможность выполнять вызов подпрограммы одного — не известного на момент компиляции — объекта из другого объекта. Механизм сигналов/слотов предоставляет удобную возможность осуществлять такие вызовы с использованием минимального объёма кода.
Суть механизма в том, что объект помимо обычных подпрограмм декларирует набор сигналов и/или слотов. К одному сигналу может быть подключено несколько слотов других объектов. Когда объекту необходимо произвести вызов подпрограмм других объектов он возбуждает сигнал. Предлагаемая библиотека самостоятельно производит вызов подключенных подпрограмм объектов.
Для примера рассмотрим типичный сценарий при программировании UI: отображение информации о приложении при нажатии кнопки. Приложение содержит объект типа Action, подпрограмма Output_About которого выводит необходимую информацию, а слот Output_About_Slot позволяет вызвать данную подпрограмму. Кроме этого приложение имеет объект Button, который возбуждает сигнал Clicked_Signal при нажатии на него пользователем.
Начнём с реализации кнопки.
private with Core.Connectables; with Core.Slots_0; private with Core.Slots_0.Emitters; package Buttons is type Button is tagged limited private; not overriding function Clicked_Signal (Self : in out Button) return not null access Core.Slots_0.Signal'Class; procedure Click (Self : in out Button'Class); private type Button is limited new Core.Connectables.Connectable_Object with record Clicked : aliased Core.Slots_0.Emitters.Emitter (Button'Unchecked_Access); end record; end Buttons;
Функция Clicked_Signal возвращает сигнал кнопки, используемых для подключения слотов других объектов. Подпрограмма Click возбуждает этот сигнал. Тип Button имеет дополнительный элемент — эмиттер сигнала.
Реализация пакета выглядит следующим образом:
package body Buttons is ----------- -- Click -- ----------- procedure Click (Self : in out Button'Class) is begin Self.Clicked.Emit; end Click; -------------------- -- Clicked_Signal -- -------------------- not overriding function Clicked_Signal (Self : in out Button) return not null access Core.Slots_0.Signal'Class is begin return Self.Clicked'Unchecked_Access; end Clicked_Signal; end Buttons;
В реализации подпрограммы Click выполняется вызов метода Emit у элемента Emitter. При выполнении этого метода происходит вызов подпрограмм подключенных слотов.
Пакет реализации вывода информации о приложении выглядит следующим образом:
private with Core.Connectables; with Core.Slots_0; private with Core.Connectables.Slots_0.Generic_Slots; package Actions is type Action is tagged limited private; not overriding procedure Output_About (Self : in out Action); function Output_About_Slot (Self : in out Action'Class) return Core.Slots_0.Slot'Class; private type Action is limited new Core.Connectables.Connectable_Object with null record; package Output_About_Slots is new Core.Connectables.Slots_0.Generic_Slots (Action, Output_About); function Output_About_Slot (Self : in out Action'Class) return Core.Slots_0.Slot'Class renames Output_About_Slots.To_Slot; end Actions;
Подпрограмма Output_About выполняет вывод информации о приложении. Подпрограмма Output_About_Slot возвращает слот для выполнения подключения к сигналу. Настраиваемый пакет Output_About_Slots предоставляет реализацию последней подпрограммы и содержит код для обработки подключения и вызова указанной подпрограммы при возбуждении сигнала.
Реализации этого пакета выглядит следующим образом:
with Ada.Text_IO; package body Actions is ------------------ -- Output_About -- ------------------ not overriding procedure Output_About (Self : in out Action) is begin Ada.Text_IO.Put_Line ("Signal/Slot in Ada demo application"); end Output_About; end Actions;
И в заключении, демонстрационная программа выглядит следующим образом:
with Actions; with Buttons; procedure Demo is A : Actions.Action; B : Buttons.Button; begin B.Clicked_Signal.Connect (A.Output_About_Slot); B.Click; end Demo;
В строке 9 производится соединение сигнала Signal_Clicked объекта B и слота Output_About_Slot объекта A.
Исходники библиотеки тут
http://forge.ada-ru.org/matreshka/browser/trunk/design/ui/source
Вадим Годунко, январь 2017г.