Виталий Ткаченко - Обратные вызовы в C++
- Название:Обратные вызовы в C++
- Автор:
- Жанр:
- Издательство:Array SelfPub.ru
- Год:2021
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Виталий Ткаченко - Обратные вызовы в C++ краткое содержание
Обратные вызовы в C++ - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Но сформировать набор результатов выполнения не так-то просто. Мы не можем перечислить в списке аргументов запрос объекта по индексу и его вызов, ведь количество объектов заранее не известно. Поэтому предварительно необходимо сформировать последовательность индексов, которая разворачивается в контексте запроса и вызова объекта. Реализация приведена в Листинг 73.
template // (1)
auto DistributeReturnImpl(std::tuple& callObjects, std::index_sequence, CallData… callData) // (2)
{
return std::tuple(std::get(callObjects)(callData…)…); // (3)
}
template // (4)
auto DistributeReturn(std::tuple callObjects, CallData… callData) // (5)
{
return DistributeReturnImpl( // (6)
callObjects, // (7)
std::make_index_sequence (), // (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.
struct FO
{
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 // (1)
class StaticDistributorVoid
{
public:
StaticDistributorVoid (CallObjects… objects) : callObjects(objects…) {} // (2)
auto& tuple() { return callObjects; } // (3)
template // (4)
void operator() (CallData… callData)
{
Distribute2(callObjects, callData…);
}
private:
std::tuple 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()
{
Интервал:
Закладка: