Виталий Ткаченко - Обратные вызовы в C++
- Название:Обратные вызовы в C++
- Автор:
- Жанр:
- Издательство:Array SelfPub.ru
- Год:2021
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Виталий Ткаченко - Обратные вызовы в C++ краткое содержание
Обратные вызовы в C++ - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
thread_local ControlPointer g_SensorControl(sensor::ISensorControl::createControl());
6.3.5. Настройка драйвера
В исходной реализации в начале работы мы создавали необходимый класс драйвера, который затем передавали интерфейсному классу (Листинг 107). Но в интерфейсах системных API мы классы использовать не можем, как поступить в этом случае? Можно предложить следующее решение: класс драйвера создавать внутри API, а в функцию настройки передавать идентификатор, в соответствии с которым будет создан соответствующий драйвер (Листинг 108).
ISensorControl sensorControl = ISensorControl::createControl;
DriverPointer driver = IDriver::createDriver(DRIVER_SIMULATION);
driver->initialize();
sensorControl->assignDriver(driver);
thread_local sensor::DriverPointer g_DriverSimulation; // (1)
thread_local sensor::DriverPointer g_DriverUSB; // (2)
thread_local sensor::DriverPointer g_DriverEthernet; // (3)
void CreateDriver(sensor::DriverType driverType, sensor::DriverPointer& driverPointer) // (4)
{
if (!driverPointer)
{
driverPointer = sensor::IDriver::createDriver(driverType);
driverPointer->initialize();
}
g_SensorControl->assignDriver(driverPointer);
}
ErrorCode assignDriver(DriverType driverType) // (5)
{
ErrorCode error = ERROR_NO;
try
{
EnumConverter conv;
conv.convert (driverType,
{sensor::DriverType::Simulation, sensor::DriverType::Usb, sensor::DriverType::Ethernet}); // (6)
if (conv.error())
{
return ERROR_INVALID_ARGUMENT;
}
switch (conv.result()) // (7)
{
case sensor::DriverType::Simulation:
{
CreateDriver(sensor::DriverType::Simulation, g_DriverSimulation);
}
break;
case sensor::DriverType::Usb:
{
CreateDriver(sensor::DriverType::Usb, g_DriverUSB);
}
break;
case sensor::DriverType::Ethernet:
{
CreateDriver(sensor::DriverType::Ethernet, g_DriverEthernet);
}
break;
}
}
catch (sensor::sensor_exception& e)
{
error = static_cast(e.code());
}
return error;
}
В строках 1–3 объявляются указатели для хранения классов всех возможных типов драйверов. В строке 4 объявлена вспомогательная функция для создания драйвера. Эта функция проверяет, создан ли драйвер соответствующего типа, при необходимости создает, инициализирует и передает его в интерфейсный класс.
В строке 5 приведена реализация интерфейсной функции для настройки драйвера. В строке 6 конвертируется переданное числовое значение в перечисление C++ (будет рассмотрено ниже). В строке 7 объявлен оператор switch, в котором анализируется полученное значение перечисления, и вызывается вспомогательная функция с соответствующими параметрами.
В функции API для задания типа драйвера используются числовые значения, а в интерфейсном классе используются перечисления C++. Для того, чтобы сконвертировать числовое значение в перечисление, используется вспомогательный класс EnumConverter(Листинг 109)
template // (1)
class EnumConverter
{
public:
template // (2)
void convert(ConvValueType value, std::initializer_list list) // (3)
{
isError_ = true;
for (Enum item : list) // (4)
{
if (static_cast(item) == value) // (5)
{
result_ = item; // (6)
isError_ = false;
break;
}
}
};
bool error() const { return isError_; }
Enum result() const { return result_; }
private:
bool isError_;
Enum result_;
};
В строке 1 объявлен шаблонный класс, параметром которого является тип перечисления. Конвертация происходит в функции 2, которая объявлена в виде шаблона, параметром шаблона является тип числового значения для конвертации. Функция принимает число, которое должно быть сконвертировано, а также список значений перечисления (строка 3). Реализация пробегает по всем элементам списка (строка 4) и, если какой-то из элементов списка перечисления равен переданному значению, запоминает это значение перечисления в качестве результата (строки 5,6).
6.3.6. Обратные вызовы
Касательно обратных вызовов мы имеем следующую ситуацию. В системном API контекст вызова передается с помощью указателей на данные, по-другому организация передачи контекста здесь невозможна (см. п. 2.1.2). В интерфейсном классе указатель на данные не используется, поскольку в C++ имеется множество гораздо более изящных способов передачи контекста. Вот тут-то нам и понадобится перенаправление вызовов (см. п. 4.6.2). Реализация одной из интерфейсных функций API, использующей перенаправление вызовов, приведена в Листинг 110.
ErrorCode readSensorValues(SensorValueCallback callback, void* pContextData)
{
ErrorCode error = ERROR_NO;
try
{
using namespace std::placeholders;
g_SensorControl->readSensorValues(std::bind(callback,_1,_2,pContextData)); // (1)
}
catch (sensor::sensor_exception& e)
{
error = e.code();
}
return error;
}
В общем-то, вся реализация заключается в вызове метода интерфейсного класса (строка 1), в который вместо непосредственно обратного вызова передается объект связывания. Функция обратного вызова, объявленная в интерфейсе API, принимает 3 входных параметра: номер датчика, значение датчика и указатель на контекст. Когда будет происходить обратный вызов, то объект связывания вызовет назначенную функцию, в которую передаст первые два параметра исходной функции, а в третий параметр будет передан переданный указатель на контекст.
6.4. Итоги
На примере разработки модуля управления датчиками кратко описаны типовые этапы проектирования: описание технического задания; оформление сценариев функционирования системы; декомпозиция и формирование архитектуры. Затем рассмотрена реализация классов с акцентом на использовании обратных вызовов. И в заключение показан процесс создания системного API и трудности, с которыми сталкивается разработчик при реализации концепции «API как оболочка». Как можно увидеть в рассмотренном примере, в практике разработки ПО существует множество ситуаций, когда целесообразно использовать обратные вызовы как элементы дизайна компонентов системы.
Заключение
Итак, наше повествование подходит к концу, пора подвести некоторые итоги.
Читать дальшеИнтервал:
Закладка: