Выбрать главу

Рис. 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 происходит распределение вызовов.

5.2.2. Передача данных

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

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

template <typename CallData>  // (1)

void Call(CallData& data)

{

}

template <typename CallData, typename First, typename…Others>  // (2)

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

{

  first(data);          // (3)

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

}

template <typename CallData, typename … CallObjects>  // (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)

  Distribute(eventID, ExternalHandler, fo, cb2cl, lambda);  // (6)

}

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

5.3. Настройка сигнатуры для передачи данных

5.3.1. Общая концепция

В рассмотренной выше реализации распределения с передачей данных (п. 5.2.2) есть один недостаток: данные, передаваемые в вызов, имеют заранее прописанную сигнатуру. В нашем случае предполагается, что это единственная числовая переменная. Если нам понадобится другая сигнатура, т. е. другой набор и типы переменных, нам придется повторять всю реализацию распределения, изменяя только сам вызов. Можно ли настроить сигнатуру, как это мы делали в универсальном аргументе? Тогда мы определяли сигнатуру с помощью пакета параметров, но теперь у нас пакет параметров используется для задания объектов вызова.

Получается, нам необходим еще один пакет параметров. В общем случае допускается объявлять шаблон функции с несколькими пакетами30, однако в этом случае для вывода типов пакета используется схема раскрытия. По этой причине необходимо, чтобы все пакеты параметров раскрывались параллельно в рамках одной синтаксической конструкции (Листинг 67), что для нашей задачи не подходит: мы должны вначале раскрыть пакет объектов вызова, а затем для каждого элемента пакета раскрыть пакет сигнатуры. Здесь нужно какое-то другое решение.

Листинг 67. Пример шаблона функции с несколькими пакетами параметров
вернуться

30

Но не шаблон класса, в шаблонах классов пакет параметров может быть только один. Кроме того, если в шаблоне объявляется пакет параметров, он должен быть последним в списке параметров шаблона.