Обратные вызовы в C++ - Виталий Евгеньевич Ткаченко 30 стр.


6.2.8. Интерфейсный класс

Класс, объявляющий интерфейс для взаимодействия с приложением, представлен в Листинг 97.

Листинг 97. Интерфейсный класс (ControlInterface.h)

namespace sensor

{


  class ISensorControl

  {

  public:

    virtual ~ ISensorControl () = default;


    virtual void initialize() = 0;  // (1)

    virtual void shutDown() = 0;    // (2)

    virtual void assignDriver(DriverPointer driver) = 0;  // (3)

    virtual DriverPointer getAssignedDriver() = 0;        // (4)

    virtual DriverPointer getSensorDriver(SensorNumber number) = 0;    // (5)

    virtual void addSensor(SensorType type, SensorNumber number) = 0;  // (6)

    virtual void deleteSensor(SensorNumber number) = 0;           // (7)

    virtual bool isSensorExist(SensorNumber number) = 0;          // (8)

    virtual bool isSensorOperable(SensorNumber number) = 0;       // (9)

    virtual SensorValue getSensorValue(SensorNumber number) = 0;  // (10)

    virtual void querySensorValue(SensorNumber number, SensorValueCallback callback) = 0;  // (11)

    virtual void readSensorValues(SensorValueCallback callback) = 0;                       // (12)

    virtual SensorValue getMinValue(SensorNumber first, SensorNumber last) = 0;  // (13)

    virtual SensorValue getMaxValue(SensorNumber first, SensorNumber last) = 0;  // (14)

    virtual void setAlert(SensorNumber number, SensorAlertCallback callback, SensorValue alertValue, AlertRule alertRule, CheckAlertTimeout checkTimeoutSeс = 1) = 0;  // (15)

      virtual void resetAlert(SensorNumber number) = 0;  // (16)


      static ISensorControl* createControl();            // (17)

  };

};


В строке 1 и 2 объявлены методы для запуска и останова. В строках 3 и 4 объявлены методы для назначения и получения драйвера. Этот драйвер должен быть создан и назначен в самом начале работы, поскольку он будет передаваться новым создаваемым датчикам. Узнать назначенный драйвер для соответствующего датчика можно в методе 5.

В строках 6 и 7 объявлены методы для добавления и удаления датчика. В методе 8 можно проверить, существует ли датчик с переданным номером, в методе 9 можно проверить, является ли датчик работоспособным.

В строке 10 объявлен метод для чтения текущего показания датчика. В методе 11 осуществляется асинхронный запрос показания датчика, значение будет возвращаться через передаваемый обратный вызов. В строке 12 осуществляется опрос показаний всех работоспособных датчиков, значения также возвращаются через обратный вызов. С помощью методов, объявленных в строках 13 и 14, можно получить минимальное и максимальное значение для набора датчиков с номерами из указанного диапазона.

В строке 15 назначается отслеживание пороговых значений, в строке 16 отслеживание выключается. С помощью метода, объявленного в строке 17, можно создать экземпляр соответствующего интерфейсного класса.


Класс, реализующий интерфейс, приведен в Листинг 98.

Листинг 98 Класс, реализующий интерфейс (SensorControl.h)

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).

Листинг 99. Обратные вызовы в классе, реализующем интерфейс (SensorControl.cpp)

void SensorControl::readSensorValues(SensorValueCallback callback)

{

  checkInitialize();  // (1)

Рассмотрим, как здесь используются обратные вызовы. Для начала самый простой случай чтение показаний работоспособных датчиков (Листинг 99).

Листинг 99. Обратные вызовы в классе, реализующем интерфейс (SensorControl.cpp)

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).

Листинг 100. Класс для анализа минимального и максимального значения (SensorControl.cpp)

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<SensorValue>::max();  // (3)

    }

    else

    {

      result_ = std::numeric_limits<SensorValue>::min();  // (4)

    }


    arrayFunMinMax_[MIN_VALUE] = &FindMinMaxValue::CompareMin;  // (5)

    arrayFunMinMax_[MAX_VALUE] = &FindMinMaxValue::CompareMax;  // (6)

  }


  void operator()(SensorNumber number, SensorPointer sensor)                  // (7)

  {

    if ( sensor->isOperable() && (number >= first_ && number <= last_) )  // (8)

    {

        (this->*arrayFunMinMax_[sign_])(sensor->getValue());              // (9)

        count_++;                                                         // (10)

    }

  }

  SensorValue result() { return result_; }  // (11)

  size_t count() { return count_; }         // (12)

private:

  SensorNumber first; // (13)

  SensorNumber last;  // (14)

  MinMaxSign sign;    // (15)

  SensorValue result; // (16)

  size_t count;       // (17)


  using FunMinMax = void (FindMinMaxValue::*)(SensorValue value);  // (18)


  void CompareMin(SensorValue value)  // (19)

  {

      if (result_ > value)

      {

          result_ = value;

      }

  }


  void CompareMax(SensorValue value)  // (20)

  {

    if (result_ < value)

    {

      result_ = value;

    }

  }


  FunMinMax arrayFunMinMax_[2];       // (21)

};


В строке 2 объявлен конструктор, который принимает на вход следующие параметры: минимальное значение диапазона номеров; максимальное значение диапазона номеров; параметр, указывающий, что необходим поиск минимального либо максимального значения. В конструкторе инициализируются переменные класса: минимальное значение диапазона (объявлено в строке 13); максимальное значение диапазона (объявлено в 14); параметр для поиска (объявлено в 15); итоговый результат (объявлено в 16); количество датчиков, которые участвовали в поиске (объявлено в 17). В зависимости от переданного параметра начальный результат инициализируется соответственно максимальным либо минимальным значением (строки 3 и 4). Кроме того, инициализируется массив указателей на функцию (строки 5 и 6, объявление в 21). Данные функции предназначены для сравнения и запоминания максимального либо минимального значений (объявлены в 19 и 20).

Анализ очередного значения происходит в перегруженном операторе 7. На вход подаются номер датчика и указатель на датчик. Если датчик работоспособный и его номер попадает в заданный диапазон номеров (строка 8), то в зависимости от параметра поиска через указатель вызывается соответствующая функция для анализа (строка 9), а также увеличивается счетчик просмотренных датчиков (строка 10). Функции 11 и 12 возвращают итоговые результаты.

Назад Дальше