Виталий Ткаченко - Обратные вызовы в C++
- Название:Обратные вызовы в C++
- Автор:
- Жанр:
- Издательство:Array SelfPub.ru
- Год:2021
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Виталий Ткаченко - Обратные вызовы в C++ краткое содержание
Обратные вызовы в C++ - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Обобщенный код– это код, реализующий заданную функциональность без привязки к типам данных.
Шаблоны объявляются ключевым словом template, после которого в угловых скобках перечисляются параметры. Параметрами шаблона могут быть как типы данных, так и значения.
Пример объявления шаблона:
template SomeTemplate
Здесь объявлен шаблон с одним параметром-типом typeи параметром-значением value.
Параметрам шаблона, как типам, так и значениям, могут быть назначены значения по умолчанию:
template SomeTemplate
После объявления шаблона следует код шаблона, в качестве которого выступает функция либо класс. В этом коде вместо имен типов и числовых значений можно подставлять имена параметров. Конкретные типы и значения, подставляемые в эти параметры, станут известны после инстанциирования шаблона, под которым понимается объявление экземпляра шаблона с заданными типами.
Инстанциирование шаблона– это объявление экземпляра шаблона с заданными типами.
Инстанциирование шаблона может быть явным и неявным. При явном инстанциировании типы параметров шаблона объявляются, а при неявном – выводятся, исходя из типов входных аргументов. Пример объявления шаблонов и их инстанциирование представлены в Листинг 23.
template // (1)
class StaticArray
{
public:
type array[size];
};
template // (2)
TYPE Sum(TYPE s2, TYPE s3)
{
return s2 + s3;
}
int main()
{
StaticArray someArray; // (3)
int a = 0; double x = 8;
Sum(a, a); // (4)
Sum (a, x); // (5)
}
В строке 1 объявлен шаблон класса, в строке 2 объявлен шаблон функции. В строке 3 производится явное инстанциирование шаблона класса, типами параметров выступают int и числовое значение. В строке 4 производится неявное инстанциирование шаблона функции, тип параметра шаблона здесь будет int, который выводится из типа входного аргумента. В строке 5 производится явное инстанциирование; оно здесь необходимо, потому что из типов входных аргументов нельзя однозначно определить, какой тип параметра должен использоваться в шаблоне.
Вообще, шаблоны в C++ – это обширная тема, заслуживающая отдельной книги, поэтому изложить ее полностью не представляется возможным. Для лучшего понимания дальнейшего материала, кроме уже изложенных базовых понятий, рекомендуется ознакомиться со следующими темами: шаблоны с переменным числом параметров; частичная специализация шаблонов; автоматический вывод типов 17 17 Для изучения можно порекомендовать книгу «Вандевурд, Джосаттис, Грегор. Шаблоны C++: справочник разработчика», где подробно рассматриваются соответствующие темы.
.
Программирование с использованием шаблонов, или, как его еще называют, метапрограммирование, достаточно сложное, поскольку предполагает высокий уровень абстракции в сочетании с неявным генерированием кода на этапе компиляции. Здесь используется другая парадигма, которая очень отличается от привычного объектно-ориентированного подхода; по своей природе шаблоны ближе к функциональному программированию. Однако именно благодаря указанным особенностям они позволяют легко и естественно решать многие задачи, в которых использование классических средств C++ порождает немало проблем.
Применительно к нашей теме, т. е. проектированию обратных вызовов, с помощью шаблонов можно реализовать множество интересных вещей, как это будет показано в следующих главах. Начнем с синхронных вызовов, как наиболее простых.
4.2. Синхронные вызовы
4.2.1. Инициатор
Проанализируем различные реализации инициатора синхронных вызовов (Листинг 24):
class Executor
{
public:
void callbackHandler(int eventID);
void operator() (int eventID);
};
using ptr_callback = void(*) (int, void*);
using ptr_callback_static = void(*) (int, Executor*);
using ptr_callback_method = void(Executor::*)(int);
void run(ptr_callback ptrCallback, void* contextData = nullptr) // (1)
{
int eventID = 0;
ptrCallback(eventID, contextData);
}
void run(ptr_callback_static ptrCallback, Executor* contextData = nullptr) // (2)
{
int eventID = 0;
ptrCallback(eventID, contextData);
}
void run(Executor* ptrClientCallbackClass, ptr_callback_method ptrClientCallbackMethod) // (3)
{
int eventID = 0;
(ptrClientCallbackClass->*ptrClientCallbackMethod)(eventID);
}
void run(Executor callbackHandler) // (4)
{
int eventID = 0;
callbackHandler(eventID);
}
Можно заметить, что все реализации, по сути, одинаковы, отличаются только типы и количество входных аргументов. Поэтому, можно попытаться сделать шаблон. Возьмем наиболее простой случай, когда функция на вход принимает только один параметр (Листинг 25):
template
void run(CallbackArgument callbackHandler)
{
int eventID = 0;
//Some actions
callbackHandler(eventID);
}
Получившийся шаблон подходит для реализации вызовов с помощью функциональных объектов (в Листинг 25 это строка номер 4), а также для лямбда-выражений. В последнем случае в качестве типа аргумента будет подставлен тип лямбда-выражения, определяемый компилятором.
Что же нам делать для остальных реализаций? Для указателей на функцию и указателей на статический метод (строки 1 и 2) можно сделать отдельный шаблон с двумя параметрами (Листинг 26):
template
void run(CallbackArgument callbackHandler, Context* context)
{
int eventID = 0;
//Some actions
callbackHandler(eventID, context);
}
Однако такое решение противоречит идее обобщенного кода: для нового типа данных мы реализуем новый код, который дублирует предыдущий, за исключением самого вызова. Как следствие, если в коде инициатора нужно сделать изменения, их придется переносить на все объявленные функции. Но это еще не все: указанное решение не покрывает случая использования указателей на метод-член класса, синтаксис вызова которого отличается от синтаксиса вызова внешней функции. Таким образом, придется реализовать еще один шаблон функции для вызова метода класса. При этом он должен будет иметь другое имя, иначе возникнет конфликт с предыдущим определением: количество входных параметров одинаково, и компилятор не знает, какую реализацию шаблона подставлять при инстанциировании.
Вот если бы мы могли для всех аргументов использовать единый общий параметр, тогда все реализации могли быть описаны с помощью одного единственного шаблона. Решить эту задачу можно путем преобразования вызовов.
Читать дальшеИнтервал:
Закладка: