Обратные вызовы в C++ - [4]
3. Как передавать контекст?
Различные способы реализации дают свои ответы на поставленные вопросы. Но прежде, чем приступить к их изучению, необходимо осветить еще несколько моментов.
1.3.2. Контекст
Вне зависимости от того, каким способом реализован исполнитель, исполняемый код всегда находится внутри тела некоторой функции. Если результат выполнения функции зависит только от входных параметров, то контекст оказывается ненужным. В качестве примера можно привести случай, когда обратный вызов возвращает результат сравнения переданных аргументов.
Однако такая ситуация встречается далеко не всегда, в большинстве случаев требуется знать значения переменных, внешних по отношению к функции исполнителя. Другими словами, необходимо получить контекст вызова.
Важность контекста можно проиллюстрировать на следующем примере. Пусть мы реализуем подсистему сетевого обмена, которая осуществляет передачу данных по каналам связи. Для управления каналом создается отдельный класс, задачей которого является формирование и отправка пакетов через вызовы соответствующих функций операционной системы. Операционная система, в свою очередь, подтверждает о доставке пакета через обратный вызов (Рис. 8). Как нам узнать в коде обработчика вызова, для какого класса предназначено подтверждение? Здесь-то и необходим контекст вызова, в качестве которого выступает указатель на класс, управляющий нужным каналом. Этот указатель не хранится внутри кода обработчика, он должен каким-то образом ему передаваться. Другими словами, обработчик вызова должен получить контекст. Различные реализации обратных вызовов предлагают свои собственные способы передачи и интерпретации контекста, которые будут подробно рассматриваться в соответствующих главах.
Рис. 8. Сетевой обмен и контекст вызова
1.4. Архитектурный дизайн вызовов
1.4.1. Синхронные и асинхронные вызовы
C точки зрения архитектурного дизайна обратные вызовы можно разделить на синхронные и асинхронные. Если при вызове какой-либо функции инициатора обратный вызов происходит внутри тела этой функции, которая затем возвращает управление, то вызов является синхронным (другое название – блокирующий). Если обратный вызов может произойти в любое время, то этот вызов является асинхронным (другое название – отложенный).
Синхронный вызов – архитектурный дизайн, в котором при вызове функции инициатора обратный вызов происходит до выхода из тела этой функции.
Асинхронный вызов – архитектурный дизайн, в котором обратный вызов может быть выполнен в любое время.
Различие между синхронными и асинхронными вызовами проиллюстрировано на Рис. 9. В первом случае поток управления входит в функцию Run, из которой вызывается функция обратного вызова, и затем управление возвращается в точку вызова. Во втором случае функция Run вначале производит сохранение аргумента, а затем выполняет некоторое действия (Action), внутри которого делает обратный вызов. В качестве действия может выступать циклический опрос, обработка очереди сообщений, создание отдельного потока и т. п.
Рис. 9. Синхронные и асинхронные вызовы: а) синхронный; б) асинхронный
Особенностью реализации синхронных вызовов является то, что здесь не нужно хранить аргумент: он передается как параметр в функцию инициатора и используется только внутри этой функции. В случае асинхронных вызовов необходима предварительная настройка аргумента, который должен быть сохранен в какой-либо нелокальной переменной.
1.4.2. Использование вызовов в API
API (Application Programming interface, интерфейс прикладных программ) – это программный код, реализующий некоторую функциональность, а также объявления, через которые некоторая программа может вызывать этот код. Указанные объявления реализуют интерфейс API.
Интерфейс API – набор объявлений для вызова кода API.
При проектировании API должны соблюдаться следующие требования.
1. Интерфейс должен следовать определённым соглашениям. Следуя указанным соглашениям, стороннее приложение может осуществлять вызовы кода API.
2. Интерфейс должен быть изолирован от реализации. Должна существовать возможность изменения кода реализации без изменения интерфейса.
3. Код должен быть подготовлен к выполнению. Для C++ это означает, что код должен быть предварительно откомпилирован.
С точки зрения C++ интерфейсы API могут быть разделены на два больших класса.
Системный API: интерфейс объявляется в виде набора функций, поддерживающих стандартный протокол вызова. Любая программа, независимо от того, на каком языке она написана, может обратиться к указанному API путем вызова функций интерфейса. Как правило, системные API реализуются в виде динамически разделяемых библиотек. В качестве примера можно назвать всем известный Windows API, реализация которого находится в системной библиотеке User32.dll. Любое приложение может загрузить эту библиотеку и вызывать требуемые функции для выполнения системных вызовов.
C++ API: интерфейс объявляется в виде набора классов C++. Как и системные, С++ API чаще всего реализуются в виде динамических библиотек, но могут поставляться также в виде статических. Использовать такие API могут только те программные компоненты, которые могут интерпретировать вызовы C++. Так, например, среда выполнения для языка Python может вызывать методы классов C++, а вот у Visual Basic такая возможность отсутствует.
Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.
Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.