Виталий Ткаченко - Обратные вызовы в C++
- Название:Обратные вызовы в C++
- Автор:
- Жанр:
- Издательство:Array SelfPub.ru
- Год:2021
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Виталий Ткаченко - Обратные вызовы в C++ краткое содержание
Обратные вызовы в C++ - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
//General specialization
template // (1)
class function;
//Partial specialization
template // (2)
class function
{
public:
Return operator()(ArgumentList… arguments) // (3)
{
}
};
В строке 1 объявлена общая специализация шаблона. Реализация класса здесь отсутствует, поскольку для каждой сигнатуры она будет различной. В строке 2 объявлен шаблон для частичной специализации, в котором два аргумента: тип возвращаемого значения и пакет параметров, передаваемых функции вызова.
В строке 3 объявлен перегруженный оператор, выступающий в качестве функции вызова. Сигнатура оператора содержит тип возвращаемого значения Returnи пакет входных параметров arguments, которые разворачиваются в список аргументов. Таким образом, в зависимости от пакета и возвращаемого значения будет сгенерирована соответствующая специализация шаблона.
Описанная реализация всего лишь демонстрирует настройку сигнатуры. Практической пользы от нее немного, потому что тело перегруженного оператора пустое, и вызов осуществлен не будет. Используя описанную технику, добавим настройку сигнатуры к аргументу, реализующему стирание типов (Листинг 48).
template
class UniArgument;
template
class UniArgument // (1)
{
private:
struct Callable
{
virtual Return operator()(ArgumentList… arguments) = 0; // (3)
};
std::unique_ptr callablePointer;
template
struct CallableObject : Callable
{
Argument storedArgument;
CallableObject(Argument argument) : storedArgument(argument) { }
Return operator() (ArgumentList… arguments) override // (8)
{
//return storedArgument(arguments…);
return std::invoke(storedArgument, arguments…); // (9)
}
};
public:
Return operator() (ArgumentList… arguments) // (10)
{
return callablePointer->operator()(arguments…); // (11)
}
template
void operator = (Argument argument)
{
callablePointer.reset(new CallableObject(argument));
}
};
По сравнению с реализацией для фиксированной сигнатуры (Листинг 45 п. 4.5.1) изменения здесь следующие. Класс аргумента (строка 1) объявляется в виде шаблона. Параметрами шаблона выступают Return– тип значения, возвращаемого функцией, и ArgumentList– пакет параметров, определяющих типы передаваемых в функцию аргументов. При объявлении перегруженных операторов (строки 3, 8, 10), вместо конкретного типа возвращаемого значения подставляется параметр шаблона Return, вместо конкретных типов входных параметров подставляется ArgumentList. В местах, где происходит вызов оператора, пакет параметров раскрывается (строки 9 и 11), что означает, что вместо argumentsбудет подставлен список переменных с типами, заданными в пакете параметров.
Теперь в универсальном аргументе можно настраивать сигнатуру, как это продемонстрировано в Листинг 49.
void ExternalHandler1(int eventID) {/*Do something*/} // (1)
int ExternalHandler2(int eventID, int contextID) { return 0; } // (2)
struct CallbackHandler // (3)
{
void operator() (int eventID) {}
bool operator() (int eventID, int contextID) { return false; }
};
int main()
{
int capturedValue = 100;
CallbackHandler callbackObject; // (4)
UniArgument argument1; // (5)
UniArgument argument2; // (6)
argument1 = ExternalHandler1; // (7)
argument2 = ExternalHandler2; // (8)
argument1 = callbackObject; // (9)
argument2 = callbackObject; // (10)
argument1 = [capturedValue](int eventID) {/*Do something*/}; // (11)
argument2 = [capturedValue](int eventID, int contextID) { /*DoSomething*/return 0; }; // (12)
argument1(3); // (13)
int res = argument2(4, 5); // (14)
return res;
}
В строках 1 и 2 объявлены две внешние функции с различными сигнатурами. В строке 3 объявлен функциональный объект, в котором перегружены операторы вызова функции с такими же сигнатурами. В строке 4 объявлен экземпляр указанного объекта.
В строках 5 и 6 объявлены универсальные аргументы, в которых с помощью параметров шаблона настраивается нужная сигнатура. Далее этим аргументам будут присваиваться различные объекты вызова в зависимости от заданной сигнатуры.
В строках 7 и 8 в аргумент передаются внешние функции. В строках 9 и 10 передается функциональный объект, у которого, в зависимости от настроенной сигнатуры будет вызван соответствующий перегруженный оператор. В строках 11 и 12 передаются лямбда-выражения. В строках 13 и 14 осуществляются вызовы в соответствии с заданной сигнатурой.
4.5.3. Вызов метода класса
В текущей реализации универсальный аргумент может работать с любыми объектами вызова, за исключением методов класса. Это связано с тем, что вызов метода класса имеет другой синтаксис, отличный от вызова функции. Как добавить поддержку вызова методов? Можно предложить следующее решение: при настройке объекта назначать указатель на метод, аналогично обычной функции, а при вызове передавать экземпляр класса как дополнительный аргумент.
До появления стандарта C++17 реализация указанного способа была достаточно сложной: пришлось бы объявлять еще один объект, который наследовался от Callableи осуществлял вызов метода; для создания соответствующего объекта пришлось бы объявить дополнительный перегруженный оператор присваивания, который в качестве входного аргумента принимал указатель на метод. Но в новом стандарте появилась функция std::invoke, которая определяет тип принимаемого объекта вызова и осуществляет вызов для соответствующего типа. Таким образом, для поддержки вызова метода класса необходимо в реализации CallableObjectизменить одну-единственную строчку:
Return operator() (ArgumentList… arguments) override // (8)
{
//return storedArgument(arguments…);
return std::invoke(storedArgument, arguments…); // (9)
}
На удивление просто, не правда ли?
Использование универсального аргумента для вызова метода класса представлено в Листинг 50.
struct CallbackHandler
{
void handler1(int eventID) {};
bool handler2(int eventID, int contextID) { return false; };
};
int main()
{
CallbackHandler callbackObject;
UniArgument argument1; // (1)
UniArgument argument2; // (2)
argument1 = &CallbackHandler::handler1; // (3)
argument2 = &CallbackHandler::handler2; // (4)
argument1(&callbackObject, 100); // (5)
argument2(&callbackObject, 0, 1); // (6)
}
В строках 1 и 2 объявлены универсальные аргументы для вызова соответствующих методов класса. Как видим, в сигнатуре функции первый параметр является типом класса, для которого будут вызываться соответствующие методы. В строках 3 и 4 производится настройка методов, в строках 5 и 6 – вызовы методов для экземпляра соответствующего класса.
Читать дальшеИнтервал:
Закладка: