initStaticMethod.setup(CallbacStaticMethod(Executor::staticCallbackHandler, &executor));  // (13)

  // (14) Pointer to the class member method

  using PtrMethod = void(Executor::*)(int);                                             // (15)

  using CallbackMemberMethod = CallbackConverter;     // (16)

  Initiator initMemberMethod;  // (17)

  initMemberMethod.setup(CallbackMemberMethod(&executor, &Executor::callbackHandler));  // (18)

  // (19) Functional object

  Initiator initFunctionObject;  // (20)

  initFunctionObject.setup(executor);      // (21)

  // (22) Lambda-expression

  auto lambda = [capturedValue](int eventID) {/*Body of lambda*/};  // (23)

  Initiator initLambda ( lambda);                 // (24)

}

В строке 1 объявлен класс – исполнитель, в котором определены необходимые нам типы вызовов: статический метод, метод-член, перегруженный оператор. В строке 2 объявлена внешняя функция, в строке 3 – экземпляр исполнителя.

В строке 4 показан обратный вызов через указатель на функцию. Объявлен тип указателя на функцию 5, тип функционального объекта для преобразования вызова 6, инстанциирование шаблона инициатора соответствующим типом 7, настройка инициатора 8. Запуск инициатора (метод run) не показан, чтобы не загромождать описание.

В строке 9 показан обратный вызов через указатель на статический метод класса. Похоже на предыдущий случай, только в качестве контекста используется указатель на класс. Объявлен тип указателя на статический метод 10, тип функционального объекта для преобразования вызова 11, инстанциирование инициатора соответствующего типа 12, настройка инициатора 13.

В строке 14 показан обратный вызов через указатель на метод-член класса. Объявлен тип указателя на метод 15, тип функционального объекта для преобразования вызова 16, инстанциирование инициатора соответствующим типом 17, настройка инициатора 18.

В строке 19 показан обратный вызов с помощью функционального объекта. Инстанциирование инициатора объявлено в строке 20, настройка инициатора – в строке 21.

В строке 22 показан обратный вызов с помощью лямбда-выражения. В строке 23 объявлено лямбда-выражение, которое запоминается в соответствующей переменной. В строке 24 инстанциирован инициатор типом лямбда-выражения. Инициатору в конструкторе передается переменная – объект указанного выражения.

Для случаев, когда используется преобразование вызовов (объявления 4, 9 и 14), можно использовать сокращенные объявления без использования промежуточных деклараций. Код в этом случае получается более компактным, но менее понятным (см. Листинг 44).

Листинг 44. Компактный способ объявлений при использовании преобразования вызовов

int main

{

  Executor executor;

  // (4) Pointer to the external function

  Initiator> initExtFunction;

  initExtFunction.setup(CallbackConverter(ExternalHandler, &executor));

  // (9) Pointer to the static method

  Initiator> initStaticMethod;

  initStaticMethod.setup(CallbackConverter(int, Executor*), Executor*>(Executor::staticCallbackHandler, &executor));

  // (14) Pointer to the class member method

  Initiator> initMemberMethod;

  initMemberMethod.setup(CallbackConverter(&executor, &Executor::callbackHandler));

}

Итак, как мы видим, для каждого типа аргумента обратного вызова нам приходится объявлять соответствующий инициатор. Может быть, можно сделать так, чтобы инициатор умел работать с различными типами аргументов? Для этого нужно спроектировать универсальный аргумент, чем мы и займемся в следующей главе.

<p>4.5. Универсальный аргумент</p><p>4.5.1. Динамический полиморфизм</p>

Для реализации универсального аргумента прежде всего необходимо обеспечить динамический полиморфизм, т. е. аргумент должен изменять свой тип в зависимости от задаваемого значения21.

Перейти на страницу:

Похожие книги