int operator() (int eventID)
{
return 10;
}
};
struct SResult
{
unsigned int code;
const char* description;
};
SResult ExternalHandler(int eventID)
{
return SResult{ 1, "this is an error" };
}
int main()
{
FO fo;
int eventID = 0;
auto lambda = [](int eventID) { return 0.0; };
auto results = DistributeReturn( std::tuple(fo, ExternalHandler, lambda), eventID); // (1)
int foRes = std::get<0>(results); // (2)
SResult ExtRes = std::get<1>(results); // (3)
double lambdaRes = std::get<2>(results); // (4)
auto [foRes1, ExtRes1, lambdaRes1] = results; // (5)
auto [foRes2, ExtRes2, lambdaRes2] = DistributeReturn(std::tuple(fo, ExternalHandler, lambda), eventID); // (6)
}
После выполнения распределения в строке 1 в переменную results помещен кортеж с результатами выполнения вызова. В строках 2, 3, 4 показано получение результатов с помощью запроса элементов кортежа по индексу, в строке 5 показано использование структурных привязок. В строке 6 показано, как можно использовать структурные привязки без промежуточной переменной results. Обход кортежа здесь не рассматривается, поскольку он был подробно описан в п. 5.3.3.
5.5. Распределитель для статического набора
5.5.1. Распределение без возврата результатов
До сих пор мы выполняли распределение с помощью функции, что вызывает определенные неудобства. Во-первых, вызов распределяющей функции получается громоздким, потому что приходится перечислять все объекты, участвующие в распределении. Во-вторых, требуются дополнительные операции, потому что в зависимости от способа настройки либо объекты вызова, либо аргументы сигнатуры необходимо упаковать в кортеж. Хорошим решением было бы предварительно сохранить нужные объекты, для чего нам понадобится распределитель в виде класса. Реализация приведена в Листинг 75.
template<typename… CallObjects> // (1)
class StaticDistributorVoid
{
public:
StaticDistributorVoid (CallObjects… objects) : callObjects(objects…) {} // (2)
auto& tuple() { return callObjects; } // (3)
template<typename… CallData> // (4)
void operator() (CallData… callData)
{
Distribute2(callObjects, callData…);
}
private:
std::tuple<CallObjects…> callObjects; // (5)
};
В строке 1 объявлен шаблон класса, параметром которого выступает пакет объектов вызова. Кортеж для хранения объектов объявлен в строке 5, он инициализируется в конструкторе 2. Для доступа к кортежу реализован метод 3, который позволяет, если необходимо, изменить его содержимое.
В строке 4 объявлен перегруженный оператор, который осуществляет распределение. Этот оператор вызывает распределяющую функцию (реализацию см. Листинг 69 п. 5.3.3), которую при желании можно сделать членом класса.
Пример использования распределителя приведен в Листинг 76.
struct FO
{
void operator() (int eventID) {}
void callbackHandler(int eventID) {}
};
void ExternalHandler(int eventID) {}
int main()
{
FO fo;
int eventID = 0;
auto lambda = [](int eventID) {};
auto callbackToMethod = std::bind(&FO::callbackHandler, fo, std::placeholders::_1);
StaticDistributorVoid distributor(ExternalHandler, fo, callbackToMethod, lambda); // (1)
distributor(eventID); // (2)
}
Как видим, использование очень простое: в строке 1 объявляется распределитель, в конструктор передаются объекты вызова, через перегруженный оператор 2 производятся вызовы сохраненных объектов.
5.5.2. Распределение с возвратом результатов
Если нужно получить значения, возвращаемые вызовами, то в распределителе необходимо модифицировать перегруженный оператор (Листинг 77).
template<typename… CallObjects> // (1)
class StaticDistributorReturn
{
public:
StaticDistributorReturn(CallObjects… objects) : callObjects(objects…) {} // (2)
auto& tuple() { return callObjects; } // (3)
template<typename… CallData> // (4)
auto operator() (CallData… callData)
{
return DistributeReturn(callObjects, callData…);
}
private:
std::tuple<CallObjects…> callObjects; // (5)
};
В строке 4 объявлен перегруженный оператор с возвращаемым типом auto. Указанный тип будет выведен из значения, возвращаемого соответствующей распределяющей функцией. (реализацию см. в Листинг 73 п. 5.4.1).