Оценить:
 Рейтинг: 0

Обратные вызовы в C++

Год написания книги
2021
<< 1 2 3 4 5 6 7 8 9 ... 13 >>
На страницу:
5 из 13
Настройки чтения
Размер шрифта
Высота строк
Поля

2.1.5. Преимущества и недостатки

Достоинства и недостатки реализации обратных вызовов с помощью указателя на функцию представлены в Табл. 1.

Табл. 1. Преимущества и недостатки обратных вызовов с указателем на функцию.

Простая реализация. Как мы видели, инициатор реализуется достаточно просто: две переменных, синтаксис вызова функции через указатель очень похож на вызов обычной функции.

Независимость инициатора и исполнителя. Любое изменение кода исполнителя никак не влияет на код инициатора, который при этом остается неизменным

Совместим с кодом на языке C. В некоторых случаях приходится разрабатывать смешанный код, т. е. часть кода пишется C, а часть – на С++. Если код исполнителя написан на C++, и этот код должен быть вызван инициатором, написанным на C, то использование указателей на функцию является единственно доступным механизмом. [4 - В качестве примера можно привести практику моделирования embedded-систем. В самом общем виде Embedded-системы представляют собой микроконтроллер, который встраивается в какое-либо устройство и выполняет функции управления, мониторинга и контроля. В силу определенных причин так сложилось, что ПО для управляющих контроллеров (такое ПО называют firmware) пишется на языке C. В процессе разработки подобных устройств часто используется моделирование, когда firmware запускается на обычном компьютере в имитационном окружении, а реальные аппаратные устройства заменяются их программными моделями. Модели и имитаторы обычно пишутся на языке C++, а firmware, как правило, написано на C – получается смешанный код.]

Подходит для реализации любых API. Можно реализовать как С++, та и системные API. Для C++ API инициатор разрабатывается в виде набора классов, для системных API – в виде набора функций.

Инициатор хранит контекст исполнителя. Как мы видели, инициатор вынужден сохранять контекст исполнителя. Это усложняет реализацию и способствует увеличению расхода памяти.

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

2.2. Указатель на статический метод класса

2.2.1. Концепция

Графическое изображение обратного вызова с помощью указателя на статический метод класса представлено на Рис. 11. Исполнитель реализуется в виде класса, код упаковывается в статический метод класса, в качестве контекста выступает указатель на экземпляр класса. При настройке указатель на статический метод как аргумент и указатель на класс как контекст сохраняются в инициаторе. Инициатор осуществляет обратный вызов посредством вызова метода, передавая ему требуемую информацию и контекст – указатель на класс.

Рис. 11. Обратный вызов с указателем на статический метод класса.

2.2.2. Инициатор

По своей сути статический метод класса – это обычная функция, ограниченная областью видимости класса. Поэтому реализация инициатора, представленная в Листинг 6, практически полностью повторяет реализацию для указателей на функцию, только в качестве контекста выступает указатель на экземпляр класса.

Листинг 6. Инициатор с указателем на статический метод класса

class Executor;  //(1)

class Initiator  // (2)

{

public:

using ptr_callback_static = void(*) (int, Executor*);                 // (3)

void setup(ptr_callback_static pPtrCallback, Executor* pContextData)  // (4)

{

ptrCallback = pPtrCallback; contextData = pContextData;           // (5)

}

void run()  //  (6)

{

int eventID = 0;

//Some actions

ptrCallback(eventID, contextData);  // (7)

}

private:

ptr_callback_static ptrCallback = nullptr;  // (8)

Executor* contextData = nullptr;            // (9)

};

В строке 1 делается предварительное объявление типа класса исполнителя. В строке 2 объявляется класс – инициатор, в строке 3 объявляется тип указателя на функцию с контекстом – экземпляром класса. В строке 4 объявлена функция для настройки указателей, соответствующие переменные (указатель на статический метод и указатель на контекст – экземпляр класса) объявлены в строках 8 и 9. В строке 6 объявлена функция запуска, внутри этой функции в строке 7 производится вызов функции по соответствующему указателю c передачей информации вызова и контекста.

2.2.3. Исполнитель

Реализация исполнителя приведена в Листинг 7.

Листинг 7. Исполнитель с указателем на статический метод класса

class Executor                    // (1)

{

public:

Executor(Initiator* initiator)  // (2)

{

initiator->setup(callbackHandler, this);

}

static void callbackHandler(int eventID, Executor* executor)  // (3)

{

//It will be called by initiator

executor->onCallbackHandler(eventID);                     // (4)
<< 1 2 3 4 5 6 7 8 9 ... 13 >>
На страницу:
5 из 13