SensorValue SensorControclass="underline" :getMinValue(SensorNumber first, SensorNumber last)
{
checkInitialize();
FindMinMaxValue fmv(first, last, FindMinMaxValue::MIN_VALUE);
sensorContainer_->forEachSensor(fmv);
return fmv.result();
}
SensorValue SensorControclass="underline" :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.
using ControlPointer = std::unique_ptr<sensor::ISensorControl>;
ControlPointer g_SensorControl(sensor::ISensorControclass="underline" :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.
#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).