Рис. 22. Рекурсивное развертывание пакета параметров для трех аргументов
Использование распределения вызовов для статического набора получателей приведено в Листинг 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).
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.
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), что для нашей задачи не подходит: мы должны вначале раскрыть пакет объектов вызова, а затем для каждого элемента пакета раскрыть пакет сигнатуры. Здесь нужно какое-то другое решение.
30
Но не шаблон класса, в шаблонах классов пакет параметров может быть только один. Кроме того, если в шаблоне объявляется пакет параметров, он должен быть последним в списке параметров шаблона.