В строке 15 назначается отслеживание пороговых значений, в строке 16 отслеживание выключается. С помощью метода, объявленного в строке 17, можно создать экземпляр соответствующего интерфейсного класса.
Класс, реализующий интерфейс, приведен в Листинг 98.
namespace sensor
{
class ISensor;
class IDriver;
class CommandQueue;
class AlertControl;
class SensorContainer;
class SensorControl: public ISensorControl
{
public:
SensorControl();
~SensorControl();
void initialize() override;
/* Other Interface methods – they are not displayed here*/
private:
SensorContainer* sensorContainer_; // (1)
CommandQueue* commandQueue_; // (2)
AlertControl* alertControl_; // (3)
bool isInitialized_; // (4)
DriverPointer driver_; // (5)
void checkInitialize(); // (6)
void checkDriver(); // (7)
};
}; //namespace sensor
В строке 1 объявлен контейнер для хранения датчиков, в строке 2 – класс для выполнения асинхронных запросов, в строке 3 – класс для отслеживания пороговых значений. Соответствующие указатели создаются в конструкторе и уничтожаются в деструкторе. Индикатор 4 указывает, была ли выполнена инициализация.
В строке 6 объявлен вспомогательный метод, который проверяет, была ли выполнена инициализация (если нет, выбрасывает исключение). В строке 7 аналогичный метод проверяет, был ли установлен драйвер.
Рассмотрим, как здесь используются обратные вызовы. Для начала самый простой случай – чтение показаний работоспособных датчиков (Листинг 99).
void SensorControl::readSensorValues(SensorValueCallback callback)
{
checkInitialize(); // (1)
sensorContainer_->forEachSensor([callback](SensorNumber number, SensorPointer sensor) // (2)
{
if (sensor->isOperable()) // (3)
{
callback(number, sensor->getValue()); // (4)
}
}
);
}
В строке 1 производится проверка, инициализирован ли класс. Если класс не проинициализирован, то функция выбросит исключение.
В строке 2 происходит перебор элементов контейнера, в качестве обратного вызова используется лямбда-выражение. Контейнер будет вызывать лямбда-выражение, в которое он будет передавать номер датчика и указатель на экземпляр класса. В теле выражения проверяется, является ли датчик работоспособным (строка 3), и если да, то выполняется соответствующий обратный вызов (строка 4).
Рассмотрим теперь поиск максимального и минимального значения для заданного диапазона номеров датчиков. Вначале разработаем вспомогательный класс, который будет последовательно принимать на вход показания датчиков и искать среди них максимальное и минимальное значение (Листинг 100).
class FindMinMaxValue
{
public:
enum MinMaxSign { MIN_VALUE = 0, MAX_VALUE = 1 }; // (1)
FindMinMaxValue(SensorNumber first, SensorNumber last, MinMaxSign sign) : // (2)
sign_(sign), first_(first), last_(last), count_(0)
{
if (sign == MIN_VALUE)
{
result_ = std::numeric_limits
}
else
{
result_ = std::numeric_limits
}
arrayFunMinMax_[MIN_VALUE] = &FindMinMaxValue::CompareMin; // (5)
arrayFunMinMax_[MAX_VALUE] = &FindMinMaxValue::CompareMax; // (6)
}
void operator()(SensorNumber number, SensorPointer sensor) // (7)
{