С помощью шаблонов невозможно реализовать интерфейсы API.
Шаблоны обеспечивают параметрический полиморфизм, что позволяет писать обобщенный код, реализующий заданную функциональность без привязки к типам данных.
Инициатор для синхронных вызовов реализуется с помощью шаблонов функций, асинхронных – с помощью шаблонов классов.
В реализации шаблона инициатора тип объекта вызова задается параметром. Поскольку разные типы объектов требуют различное число параметров и используют неодинаковый синтаксис, для сохранения единой реализации используется преобразование вызовов.
Функциям, реализующим алгоритмы, зачастую требуются различные операции над данными. Поскольку в обобщенном коде типы данных заранее не известны, для реализации операций используются предикаты.
В асинхронных вызовах для каждого типа аргумента приходится инстанциировать соответствующий инициатор. Использование универсального аргумента позволяет реализовать единый класс для любых типов аргументов, однако в некоторых случаях это может привести к падению производительности.
В стандартной библиотеке STL имеются мощные средства для организации вызовов, реализующие универсальный аргумент, вызов методов класса, перенаправление вызовов.
Шаблонам присущи недостатки, большинство из которых незначительны и не перевешивают их достоинств. Однако шаблоны не предполагают предварительно откомпилированного кода, и по этой причине не могут использоваться в интерфейсах API.
Под распределением вызовов понимается техника, в которой при вызове единственной функции осуществляется выполнение множества вызовов через соответствующие аргументы.
Графически задача распределения вызовов показана на Рис. 21. Компонент, осуществляющий вызов, называется источником; аргументы вызова называются получателями; компонент, осуществляющий распределение вызовов, называется распределитель; код, запускающий вызовы, называется распределяющая функция. При необходимости дополнительно в вызов могут передаваться какие-либо данные.
Распределитель может быть реализован в виде функции либо класса. Если распределитель реализован в виде функции, то он сам представляет собой распределяющую функцию. Если распределитель реализован в виде класса, то распределяющая функция представляет собой метод класса либо перегруженный оператор.
Рис. 21. Распределение вызовов
Как видим, постановка задачи звучит достаточно просто. Зачем же тогда ей посвящен отдельный раздел? Во-первых, распределение вызовов имеет важное прикладное значение: оно используется в самых различных приложениях, таких, как обработка команд, оповещение о событиях, синхронизация операций и др. Во-вторых, задача распределения вызовов совсем не такая простая, как это может показаться из формального описания. Для ее решения используются изощренные техники, призванные обеспечивать максимальную эффективность для самых различных требований.
Итак, рассмотрим, как реализуется распределение вызовов.
5.2. Статический набор получателей
5.2.1. Распределение в статическом наборе
Если типы и количество получателей известны на этапе компиляции и не планируется их изменение в процессе выполнения программы, то мы имеем статический набор получателей. В этом случае распределитель можно реализовать в виде шаблонной функции, которая в качестве входных аргументов будет принимать объекты вызова. Но поскольку типы объектов и их количество могут быть различными, логично в качестве входного параметра функции использовать пакет, задаваемый шаблоном.
Итак, нам необходимо выполнить вызов для каждого объекта, входящего в пакет. Для решения этой задачи используется техника рекурсивного развертывания пакета, суть которой заключается в следующем.
Объявляется функция, первым параметром которой выступает объект вызова, а вторым – пакет. Когда на вход данной функции поступает пакет, первый объект из него извлекается, происходит вызов этого объекта, а затем функция рекурсивно вызывается вновь с пакетом, содержащим еще не извлеченные объекты. Когда в результате рекурсивных вызовов все объекты будут извлечены, будет вызвана функция, на вход которой будет передан пустой пакет. Данная функция завершает рекурсивное выполнение.
Читать дальше