Обратные вызовы в C++ - [48]

Шрифт
Интервал


Итак, класс для анализа готов. Теперь можно вызвать метод для итерации по элементам контейнера, и в качестве обратного вызова передать экземпляр соответствующего вспомогательного класса. Метод будет вызывать перегруженный оператор, и таким образом, мы узнаем минимальное либо максимальное значение (Листинг 101).

Листинг 101. Поиск минимального и максимального значений (SensorControl.cpp)

>SensorValue SensorControl::getMinValue(SensorNumber first, SensorNumber last)

>{

>  checkInitialize();


>  FindMinMaxValue fmv(first, last, FindMinMaxValue::MIN_VALUE);

>  sensorContainer_->forEachSensor(fmv);

>  return fmv.result();

>}


>SensorValue SensorControl::getMaxValue(SensorNumber first, SensorNumber last)

>{

>  checkInitialize();


>  FindMinMaxValue fmv(first, last, FindMinMaxValue::MAX_VALUE);

>  sensorContainer_->forEachSensor(fmv);

>  return fmv.result();

>}

6.3. Разработка системного API

6.3.1. API как оболочка

Уже после того, как классы модуля были разработаны, протестированы и начали использоваться в системе, появилось новое требование – ввести поддержку системного API. Как известно, в интерфейсах системных API можно использовать только внешние функции и простые структуры данных в стиле C; классы и другие специфические конструкции C++ использовать нельзя (см. п. 1.4.2). Так что же, все теперь придется переписывать? Можно предложить следующее решение: использовать интерфейс API как оболочку для вызова методов класса. Концептуальный пример приведен в Листинг 102.

Листинг 102. Концептуальный пример реализации API как оболочки

>using ControlPointer = std::unique_ptr;

>ControlPointer g_SensorControl(sensor::ISensorControl::createControl());


>void initialize () // This function is declared in the header file as part of API interface

>{

>  g_SensorControl->initialize();

>}


Однако не все так просто, перед нами встают следующие проблемы.

1. В исходной реализации мы использовали специфические типы C++, такие, как std::function, smart pointers и т. п., что не допускается в интерфейсах системных API. Какие типы использовать взамен?

2. Для обработки ошибок в исходной реализации мы использовали исключения. Как сейчас обрабатывать ошибки, ведь в интерфейсах API исключения недопустимы?

3. В исходной реализации мы в каждом потоке могли объявить отдельный интерфейсный класс и работать с ним независимо от остальных потоков. Как теперь обеспечить многопоточную работу, ведь отдельные потоки вызывают одни и те же интерфейсные функции?

4. В исходной реализации драйвер настраивался путем создания нового класса и передаче его в интерфейсный класс. Как теперь настраивать драйвер, если в интерфейсах API нельзя использовать классы?

5. Как организовать обратные вызовы?


Рассмотрим, как эти проблемы можно решить.

6.3.2. Объявления типов

В исходной реализации общие типы объявлены в SensorDef.h, но мы не можем просто перенести их в интерфейс API из-за использования специфических конструкций С++. Поэтому нам придется повторить эти объявления в стиле C с использованием простых типов, которые можно будет использовать в интерфейсных функциях. Объявления представлены в Листинг 103.

Листинг 103. Объявления типов для интерфейса API (SensorLib.h)

>#ifdef _WINDOWS  // (1)

>  #ifdef LIB_EXPORTS

>    #define LIB_API __declspec(dllexport)

>  #else

>    #define LIB_API __declspec(dllimport)

>  #endif

>  #else

>    #define LIB_API

>#endif


>typedef uint32_t SensorNumber;       // (2)

>typedef double SensorValue;          // (3)

>typedef uint32_t CheckAlertTimeout;  // (4)


>typedef uint32_t SensorType;         // (5)

>typedef uint32_t DriverType;         // (6)

>typedef uint32_t AlertRule;          // (7)


>typedef void(*SensorValueCallback)(SensorNumber, SensorValue, void*);               // (8)

>typedef CheckAlertTimeout(*SensorAlertCallback)(SensorNumber, SensorValue, void*);  // (9)

>typedef SensorValue(*OnSimulateReadValue)(SensorNumber, int, void*);                // (10)

>typedef int (*OnSimulateOperable)(SensorNumber, void*);                             // (11)


>enum eSensorType  // (12)

>{

>  SENSOR_SPOT = 0,

>  SENSOR_SMOOTH = 1,

>  SENSOR_DERIVATIVE = 2,

>};


>enum eDriverType  // (13)

>{

>  DRIVER_SIMULATION = 0,

>  DRIVER_USB = 1,

>  DRIVER_ETHERNET = 2

>};


>enum  eAlertRule  // (14)

>{

>  ALERT_MORE = 0,

>  ALERT_LESS = 1

>};


В строке 1 объявлены определения для экспортируемых функций. Эти объявления необходимы для компиляции динамической библиотеки в среде Windows, для других платформ они неактуальны.

В строках 2–4 объявлены типы, которые будут использоваться для входных параметров интерфейсных функций. Это те же объявления, которые использовались в исходной реализации (SensorDef.h, см. п. 6.2.2).

В строках 5–7 вместо перечислений C++ объявляются простые числовые типы. В экспортируемых функциях нежелательно использовать перечисления как типы входных параметров, потому что размер этих типов в C явно не определен. Вместо этого перечисления используются в качестве числовые констант, они объявлены соответственно в строках 12–14.

В строках 8–11 объявлены типы указателей на функцию для выполнения обратных вызовов. Как видим, в отличие от исходной реализации здесь присутствует дополнительный параметр для указания контекста вызова.


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


Геймдизайн. Рецепты успеха лучших компьютерных игр от Super Mario и Doom до Assassin’s Creed и дальше

Что такое ГЕЙМДИЗАЙН? Это не код, графика или звук. Это не создание персонажей или раскрашивание игрового поля. Геймдизайн – это симулятор мечты, набор правил, благодаря которым игра оживает. Как создать игру, которую полюбят, от которой не смогут оторваться? Знаменитый геймдизайнер Тайнан Сильвестр на примере кейсов из самых популярных игр рассказывает как объединить эмоции и впечатления, игровую механику и мотивацию игроков. Познакомитесь с принципами дизайна, которыми пользуются ведущие студии мира! Создайте игровую механику, вызывающую эмоции и обеспечивающую разнообразие.


Обработка событий в С++

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


MFC и OpenGL

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Симуляция частичной специализации

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.