15.1. Применение указателей функций для их обратного вызова
15.1. Применение указателей функций для их обратного вызова
Проблема
Планируется использование некоторой функции func1, которая на этапе выполнения должна вызывать другую функцию func2. Однако по той или иной причине нельзя внутри функции func1 жестко закодировать имя функции func2. Возможно, имя функции func2 неизвестно на этапе компиляции, или func1 относится к программному интерфейсу независимого разработчика, и она не может быть изменена и перекомпилирована В любом случае вам придется воспользоваться функцией обратного вызова (callback function).
Решение
При использовании указанных выше функций объявите func1 с указателем на функцию в качестве своего аргумента и передайте ей адрес func2 на этапе выполнения. Используйте typedef, чтобы программа легче читалась и отлаживалась. Пример 15.1 показывает, как можно реализовать функцию обратного вызова, используя указатель на функцию.
Пример 15.1. Функция обратного вызова
#include <iostream>
// Пример функции обратного вызова
bool updateProgress(int pct) {
std::cout << pct << "% complete... ";
return(true);
}
// Этот typedef делает программный код более понятным
typedef bool (*FuncPtrBoolInt)(int);
// Функция, которая выполняется достаточно длительное время
void longOperation(FuncPtrBoolInt f) {
for (long l=0; l < 100000000; l++)
if (l % 10000000 == 0)
f(l/1000000);
}
int main() {
longOperation(updateProgress); // нормально
}
Обсуждение
В ситуации, которая показана в примере 15.1, применение указателя на функцию является хорошим решением, если UpdateProgress и longOperation ничего не должны знать друг о друге. Например, функцию, которая обновляет индикатор состояния процесса в диалоговом окне пользовательского интерфейса (user interface — UI), в окне консольного режима или где-то еще, не заботит контекст, в котором она вызывается. Аналогично функция longOperation может быть частью некоторого программного интерфейса загрузки данных, которого не заботит место вызова: из графического UI, из окна консольного режима или из фонового процесса.
Сначала потребуется определить сигнатуру функции, которую вы планируете вызывать, и создать для нее typedef. Оператор typedef — ваш помощник в тех случаях, когда приходится иметь дело с указателями функций, потому что они имеют не очень привлекательный синтаксис. Рассмотрим, как обычно объявляется такой указатель на примере переменной f, которая содержит адрес функции, принимающей единственный аргумент целого типа и возвращающей значения типа boolean. Это может выглядеть следующим образом
bool (*f)(int); // f - имя переменной
Вы можете справедливо возразить, что здесь нет ничего особенного и я просто излишне драматизирую ситуацию. Но что вы скажете, если требуется определить вектор vector таких указателей?
vector<bool (*)(int)> vf;
Или их массив?
bool (*af[10])(int);
Форма представления указателей на функции отличается от обычных переменных С++, которые обычно задаются в виде (квалифицированного) имени типа, за которым идет имя переменной. Поэтому они вносят путаницу при чтении программного кода.
Итак, в примере 15.1 я использовал следующий typedef.
typedef bool (*FuncPtrBoolInt)(int);
Сделав это, я могу свободно объявлять указатели функций с сигнатурой, возвращающей значение bool и принимающей единственный аргумент, как это я бы делал для параметра любого другого типа, например.
void longOperation(FuncPtrBoolInt f) { // ...
Теперь все, что надо сделать в longOperation, — это вызвать f, как если бы это была любая обычная функция.
f(l/1000000);
Таким образам, здесь f может быть любой функцией, которая принимает аргумент целого типа и возвращает bool. Предположим, что в вызывающей функции longOperation не требуется обеспечивать продвижение индикатора состояния процесса. Тогда ей можно передать указатель на функцию без операций.
bool whoCares(int i) {return(true);}
//...
longOperation(whoCares);
Более важно то, что выбор функции, передаваемой longOperation, может осуществляться динамически на этапе выполнения.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Контекст системного вызова
Контекст системного вызова Как уже обсуждалось в главе 3, "Управление процессами", при выполнении системного вызова ядро работает в контексте процесса. Указатель current указывает на текущее задание, которое и есть процессом, выполняющим системный вызов.В контексте процесса
Семантика вызова
Семантика вызова Вызов локальной процедуры однозначно приводит к ее выполнению, после чего управление возвращается в головную программу. Иначе дело обстоит при вызове удаленной процедуры. Невозможно установить, когда конкретно будет выполняться процедура, будет ли она
Конфигурация зоны для обратного преобразования
Конфигурация зоны для обратного преобразования В листинге указано несколько зон, некоторые из них предназначены для обратного преобразования. Эти зоны позволяют серверу DNS определять доменное имя по IP-адресу. Для того чтобы это стало возможным, необходимо создать
§ 150. От обратного
§ 150. От обратного 8 июля 2008Один из самых эффективных дизайнерских приемов — придумывание неудобных, непонятных и запутанных решений.Например, дизайнеру нужно сделать идеальный вход в магазин. С чего начать? Во-первых, дверь не должна быть видна, лучше всего, если она
1.1.5. Функции обратного вызова
1.1.5. Функции обратного вызова Прежде чем двигаться дальше, необходимо разобраться с тем, что такое функции обратного вызова (callback functions: этот термин иногда также переводят "функции косвенного вызова"). Эти функции в программе описываются, но обычно не вызываются напрямую,
ГЛАВА 8. Интерфейсы обратного вызова, делегаты и события
ГЛАВА 8. Интерфейсы обратного вызова, делегаты и события До этого момента в нашей книге в каждом примере приложении программный код Main() тем или иным способом направлял запросы соответствующим объектам. Но мы пока что не рассматривали возможность обратного обращения
Программирование с помощью таймеров обратного вызова
Программирование с помощью таймеров обратного вызова Во многих приложениях возникает необходимость вызывать конкретный метод через регулярные промежутки времени. Например, в одном приложении может потребоваться отображение текущего времени в строке состояния с
Поддержка синхронного вызова
Поддержка синхронного вызова Генерируемый агент определяет также поддержку синхронного вызова Web-методов. Например, синхронный вариант метода Subtract() реализуется так.public int Subtract(int x, int y) { object[] results = this.invoke("Subtract", new object[] {x, y}); return ((int)(results[0]));}Обратите внимание на то, что
Совет 7. При использовании контейнеров указателей, для которых вызывался оператор new, не забудьте вызвать delete для указателей перед уничтожением контейнера
Совет 7. При использовании контейнеров указателей, для которых вызывался оператор new, не забудьте вызвать delete для указателей перед уничтожением контейнера Контейнеры STL отличаются умом и сообразительностью. Они поддерживают итераторы для перебора как в прямом, так и в
Совет 46. Передавайте алгоритмам объекты функций вместо функций
Совет 46. Передавайте алгоритмам объекты функций вместо функций Часто говорят, что повышение уровня абстракции языков высокого уровня приводит к снижению эффективности сгенерированного кода. Александр Степанов, изобретатель STL, однажды разработал небольшой комплекс
12.3.5. Адаптеры функций для объектов-функций
12.3.5. Адаптеры функций для объектов-функций В стандартной библиотеке имеется также ряд адаптеров функций, предназначенных для специализации и расширения как унарных, так и бинарных объектов-функций. Адаптеры – это специальные классы, разбитые на следующие две
15.5. Оператор вызова функции
15.5. Оператор вызова функции Оператор вызова функции может быть перегружен для объектов типа класса. (Мы уже видели, как он используется, при рассмотрении объектов-функций в разделе 12.3.) Если определен класс, представляющий некоторую операцию, то для ее вызова
19.11.2. Вызов функций из файла функций
19.11.2. Вызов функций из файла функций Мы уже рассматривали, каким образом функции вызываются из командной строки. Эти типы функций обычно используются утилитами, создающими системные сообщения.А теперь воспользуемся снова описанной выше функцией, но в этом случае
Обратного пути нет
Обратного пути нет Можно было бы ожидать, что допустимо и обратное переопределение атрибута в функцию без аргументов. Но нет. Присваивание - операция применимая к атрибутам, - становится бессмысленной для функций. Предположим, что a - это атрибут класса C, и некоторая