template < typename First, typename…Others>

void Call(First& first, Others…rest)  // (2)

{

  first();            // (3)

  Call(rest…);      // (4)

}

template 

void Distribute(CallObjects… objects)  // (5)

{

  Call(objects…);  // (6)

}

Графически развертывание пакета параметров для трех аргументов изображено на Рис. 22. Процесс начинается с вызова распределяющей функции, которая объявлена в строке 5. Здесь используется пакет параметров objects, который содержит объекты вызова. Внутри этой функции, в строке 6, происходит первый вызов рекурсивной функции, которой на вход передаются соответствующий аргумент в виде пакета.

Рекурсивная функция Call объявлена в строке 2. Эта функция принимает два аргумента: первый параметр из пакета first и пакет остальных параметров rest. При первом вызове пакет параметров из Distribute передается в эту функцию, и там происходит его распаковка: первый параметр извлекается и помещается в first, оставшаяся часть пакета записывается в rest. В строке 3 производится вызов, а пакет с оставшимися параметрами передается в рекурсивный вызов Call (строка 4).

Итак, на каждом шаге рекурсивного вызова из пакета извлекается очередной параметр, а размер исходного пакета уменьшается. Таким образом, в итоге все параметры будут извлечены, и пакет станет пустым. Эта ситуация обрабатывается путем объявления функции с пустым пакетом параметров, т. е. функции, которая на вход не принимает ни одного аргумента (строка 1). Тело этой функции пустое, в ней происходит возврат управления, и по цепочке рекурсивных вызовов управление возвращается в исходную точку в строке 6.

Рис. 22. Рекурсивное развертывание пакета параметров для трех аргументов

Использование распределения вызовов для статического набора получателей приведено в Листинг 64.

Листинг 64. Распределение вызова для статического набора

void ExternalHandler()  // (1)

{

}

struct FO

{

  void callbackHandler() {}

  void operator() () {}

};

int main()

{

  FO fo;                             // (2)

  auto lambda = []() {};             // (3)

  auto cb2cl = std::bind(&FO::callbackHandler, fo);  // (4)

  Distribute(ExternalHandler, fo, cb2cl, lambda);    // (5)

}

В строках 1, 2, 3, 4 объявлены соответствующие объекты вызова: внешняя функция, функциональный объект, лямбда-выражение, объект для вызова метода класса. Для вызова метода класса в строке 4 объявляется объект связывания (см. п. 4.6.6), в строке 5 происходит распределение вызовов.

<p>5.2.2. Передача данных</p>

Если в вызов необходимо передавать данные, то для этого в описанные выше функции необходимо ввести дополнительный параметр (Листинг 65).

Листинг 65. Распределяющая функция для статического набора получателей с передачей данных

template   // (1)

void Call(CallData& data)

{

}

template   // (2)

void Call(CallData data, First& first, Others&…rest)

{

  first(data);          // (3)

  Call(data, rest…);  // (4)

}

template   // (5)

void Distribute(CallData data, CallObjects… objects)

{

  Call(data, objects…);  // (6)

}

Приведенная реализация повторяет Листинг 63 п. 5.2.1, только теперь в функциях к объектам вызова добавляется параметр data для передачи данных.

Пример распределения для статического набора получателей с передачей данных представлен в Листинг 66.

Листинг 66. Распределение вызовов для статического набора получателей

void ExternalHandler(int eventID)  // (1)

{

}

struct FO

{

  void callbackHandler(int eventID) {}

  void operator() (int eventID) {}

};

int main()

{

  using namespace std::placeholders;

  FO fo;  // (2)

  auto lambda = [](int eventID) {};                      // (3)

  auto cb2cl = std::bind(&FO::callbackHandler, fo, _1);  // (4)

  int eventID = 0;  // (5)

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

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