> }
>private:
> Executor* ptrCallbackClass = nullptr; // (8)
> ptr_callback_method ptrCallbackMethod = nullptr; // (9)
>};
В строке 1 делается предварительное объявление типа класса исполнителя. В строке 2 объявляется класс-инициатор, в строке 3 объявляется тип указателя для класса-исполнителя. В строке 4 объявляется функция для настройки указателей, соответствующие переменные (указатель на метод класса и указатель на экземпляр класса) объявлены в строках 8 и 9. В строке 6 объявлена функция запуска, внутри этой функции в строке 7 через соответствующий указатель производится вызов метода класса.
2.3.3. Исполнитель
Реализация исполнителя приведена в Листинг 11.
>class Executor // (1)
>{
>public:
> void callbackHandler(int eventID) // (2)
> {
> //It will be called by initiator
> }
>};
>int main() // (3)
>{
> Initiator initiator; // (4)
> Executor executor; // (5)
> initiator.setup(&executor, &Executor::callbackHandler); // (6)
> initiator.run(); // (7)
>}
В строке 1 объявляется класс-исполнитель. В строке 2 объявлен метод класса, который будет выполнять функцию обработчика обратного вызова. В указанный метод передается информация вызова (в нашем случае это eventID). В строке 3 объявлена основная функция, в которой осуществляются все необходимые операции. В строке 4 объявлен класс-инициатор, в строке 5 объявлен класс-исполнитель. В строке 6 осуществляется настройка обратного вызова, в строке 7 производится запуск инициатора.
2.3.4. Управление контекстом
Рассматриваемая реализация позволяет осуществлять управление контекстом тремя способами: настройка экземпляра класса-исполнителя, настройка указателя на метод, переопределение виртуальных функций. Это приводит к интересным эффектам.
Пусть у нас будут объявления классов-исполнителей с наследованием, как показано в Листинг 12. Графически иерархия наследования изображена на Рис. 13.
>class Executor
>{
>public:
> virtual void callbackHandler1(int eventID);
> virtual void callbackHandler2(int eventID);
>};
>class Executor1: public Executor
>{
>public:
> void callbackHandler1(int eventID) override;
>};
>class Executor2: public Executor
>{
>public:
> void callbackHandler2(int eventID) override;
>};
>class Executor3: public Executor1, public Executor2
>{
>};
Рис. 13. Иерархия наследования классов-исполнителей
Итак, будем назначать различные указатели на экземпляры классов и методы-члены, как показано в Листинг 13.
>int main()
>{
> Initiator initiator;
> Executor executor;
> Executor1 executor1;
> Executor2 executor2;
> Executor3 executor3;
> initiator.setup(&executor, &Executor::callbackHandler1); // (1)
> initiator.setup(&executor, &Executor::callbackHandler2); // (2)
> initiator.setup(&executor1, &Executor::callbackHandler1); // (3)
> initiator.setup(&executor1, &Executor::callbackHandler2); // (4)
> initiator.setup(&executor2, &Executor::callbackHandler1); // (5)
> initiator.setup(&executor2, &Executor::callbackHandler2); // (6)
> //initiator.setup(&executor3, &Executor::callbackHandler1); //Incorrect, base class is ambiguous // (7)
> //initiator.setup(&executor3, &Executor::callbackHandler2); //Incorrect, base class is ambiguous // (8)
> initiator.setup((Executor1*)&executor3, &Executor::callbackHandler1); // (9)
> initiator.setup((Executor1*)&executor3, &Executor::callbackHandler2); // (10)