Реализация описанной техники приведена в Листинг 63.
Листинг 63. Распределяющая функция для статического набора получателей
void Call() // (1)
{
}
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 происходит распределение вызовов.
Если в вызов необходимо передавать данные, то для этого в описанные выше функции необходимо ввести дополнительный параметр (Листинг 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)
Distribute(eventID, ExternalHandler, fo, cb2cl, lambda); // (6)
}
В строках 1, 2, 3, 4 объявлены соответствующие объекты вызова: внешняя функция, функциональный объект, лямбда-выражение, объект для вызова метода класса. Для вызова метода класса в строке 4 объявляется объект связывания (см. п. 4.6.6), в строке 5 объявляется переменная для передачи данных. В строке 6 происходит распределение вызовов, первым параметром передается аргумент данных eventID.
Читать дальше