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

Поскольку возвращаемые значения могут иметь различные типы, напрашивается сохранять их в кортеже, который затем будет возвращаться как результат работы распределяющей функции. Но мы же не знаем заранее типы возвращаемых значений, их определяют объекты вызова. Какие тогда типы задавать при инстанциировании переменной-кортежа? Можно предложить следующее решение: при объявлении кортежа не указывать явно хранимые в нем типы, а в конструктор в качестве входных аргументов передать результаты выполнения вызовов. В этом случае типы элементов кортежа будут выведены автоматически.

Но сформировать набор результатов выполнения не так-то просто. Мы не можем перечислить в списке аргументов запрос объекта по индексу и его вызов, ведь количество объектов заранее не известно. Поэтому предварительно необходимо сформировать последовательность индексов, которая разворачивается в контексте запроса и вызова объекта. Реализация приведена в Листинг 73.

Листинг 73. Распределение вызовов с возвратом результатов

template <typename… CallObjects, std::size_t… indices, typename…CallData>              // (1)

auto  DistributeReturnImpl(std::tuple<CallObjects…>& callObjects, std::index_sequence<indices…>, CallData… callData)  // (2)

{

  return std::tuple(std::get<indices>(callObjects)(callData…)…);                         // (3)

}

template<typename… CallObjects, typename…CallData>                               // (4)

auto DistributeReturn(std::tuple<CallObjects…> callObjects, CallData… callData)  // (5)

{

  return DistributeReturnImpl(                                                     // (6)

    callObjects,                                                                   // (7)

    std::make_index_sequence<sizeof…(CallObjects)> (),                           // (8)

    callData…);                                                                  // (9)

}

Шаблон распределяющей функции объявлен в строке 4, параметрами шаблона являются пакет объектов вызова и пакет данных вызова. Сама функция объявлена в строке 5, входными параметрами являются кортеж вызываемых объектов, параметризованный пакетом объектов, и пакет данных вызова. Возвращаемое значение функции объявлено как auto, что означает, что оно будет выводиться из возвращаемого значения.

Для использования рассматриваемого распределения появляется требование, чтобы все объекты вызова возвращали результаты. Это связано с тем, что кортеж не может хранить типы void. Для вызовов, которые не возвращают результат, можно использовать любой из способов, описанный в главе 5.3.

В строке 6 вызывается вспомогательная функция, которой передается кортеж объектов вызова 7, последовательность индексов 8, данные вызова 9. Последовательность индексов формируется с помощью конструкции std::make_index_sequence, которой на вход в качестве значения передается размер пакета вызываемых объектов (определяется с помощью sizeof…).

В строке 1 объявлен шаблон вспомогательной функции, параметрами шаблона выступают пакет объектов вызова CallObjects, пакет индексов Indices и пакет данных вызова CallData. Сама функция объявлена в строке 2, ее входными параметрами являются: кортеж вызываемых объектов, параметризованный пакетом объектов вызова; последовательность индексов, параметризованная пакетом индексов; пакет данных вызова. Данная функция возвращает кортеж, сформированный по результатам вызова. Для получения элемента кортежа используется вызов std::get, на вход которому передается индекс элемента, и затем происходит вызов полученного элемента, на вход которому передаются данные callData. А поскольку вместо конкретного индекса мы используем последовательность индексов, она будет развернута в набор вызовов get с соответствующими индексами, таким образом, осуществляя вызовы для все элементов кортежа в соответствии с их индексами. Графически рассмотренная операция для трех объектов изображена на Рис. 23.

Рис. 23. Формирование кортежа возвращаемых значений

5.4.2. Анализ результатов

Итак, мы получили возвращаемые значения в виде кортежа. Как нам проанализировать полученные результаты? Существуют следующие способы анализа содержимого кортежа:

• доступ к элементам кортежа по индексу с помощью std::get;

• обход кортежа;

• использование структурных привязок.

Пример анализа значений, возвращаемых распределением вызовов, приведен в Листинг 74.

Листинг 74. Анализ возвращаемых значений

struct FO

{