Типобезопасные обратные вызовы в C++

ОГЛАВЛЕНИЕ

В данной статье представлен класс, который добавляет типобезопасные обратные вызовы C++ в проекты.

•    Скачать исходники - 32.39 Кб
•    Скачать документацию - 33.08 Кб

Обсуждаемый класс добавляет типобезопасные обратные вызовы C++ в проекты. Его свойства заключаются в следующем:
•    Любую функцию в любом классе можно вызвать откуда угодно в любом другом классе.
•    Можно передать от 0 до 5 аргументов любого типа функции обратного вызова и задать любой тип возвращаемой переменной.
•    Обратный вызов можно передать в качестве аргумента любой функции.
•    Оптимизирован для высокой скорости.
•    Размер кода менее 1 Кб, не нужны дополнительные библиотеки.
•    Не зависит от платформы: работает на Windows, Linux, Mac, и т.д.
•    Был испытан в Visual Studio 6.0, 7.0, 7.1 и 8.0 (= Visual Studio 6, вплоть до .NET 2005).
•    Новое в версии 3.0 (октябрь 2007): класс также поддерживает обратные вызовы статических функций и функций внутри виртуально производных классов.

Введение

В C++ простого адреса функции недостаточно для определения обратного вызова, как в старом C. В C++ каждый экземпляр класса хранит переменные класса в своей собственной области памяти. Указатель this указывает на эту область переменных. При каждом вызове любой функции C++ указатель this невидимо передается функции и в дополнение к аргументам функции. Microsoft Visual Studio 6 использует регистр процессора ECX для передачи указателя this, тогда как нормальные аргументы функции проталкиваются в стек. Чтобы использовать обратные вызовы, скопируйте файлы Callback.h и PreProcessor.h в проект и #include "Callback.h".

Зачем использовать обратные вызовы?

Допустим, написан планировщик, реагирующий на определенные события путем выполнения соответствующих действий. События могут быть любыми, например, прибытие данных, ввод данных пользователем или таймеры, а действия являются выполнением любого кода. Планировщик содержит в дополнительном потоке бесконечный цикл, ждущий события. В Windows можно использовать функцию API WaitForMultipleObjects(), возвращающую индекс сигнализированного события. Планировщик выглядит следующим образом:

Пример кода, сопровождающий данную статью, использует обратные вызовы для сортировки списка разными функциями сортировки обратного вызова. Заметьте, что благодаря сигналам и слотам не нужно писать собственную функцию RegisterCallback()! Более подробно читайте в части 2 данной серии статей.

Определение обратных вызовов

В callback.h есть два класса: cCall и cCallGen. Происходящее внутри этих классов весьма сложно, поэтому здесь объясняется только применение. cCall определяется как:

cCall <_ReturnType, _Arg1, _Arg2, _Arg3, _Arg4, _Arg5>

Можно использовать функции обратного вызова, принимающие 0, 1, 2, 3, 4 или 5 аргументов. Если нужно больше 5 аргументов, классы обратного вызова с легкостью расширяются. Однако рекомендуется передавать более 5 аргументов в виде структуры, чтобы код легче читался.

Допустим, надо осуществить обратный вызов следующей функции:

int cMyApplication::Calculate(float Factor, bool Flag, char* Name)
{
    ......
}

Для этой функции можно создать соответствующий обратный вызов:

cCall <int, float, bool, char*> i_CallbackCalculate;

Первый аргумент шаблона (int) всегда используется для определения типа возвращаемой переменной. Следующие аргументы шаблона (float, bool, char*) являются типами аргументов, передаваемых функции обратного вызова.

Чтобы определить обратный вызов для следующей функции без аргументов, возвращающей void,

void cMyApplication::Print()
{
    ......
}

надо написать:

cCall <void> i_CallbackPrint;

Присваивание обратных вызовов

Обратные вызовы могут присваиваться друг другу с помощью operator=, но из-за типобезопасности этих обратных вызовов следующее приводит к ошибке компилятора:

i_CallbackCalculate = i_CallbackPrint;  // ошибка
i_CallbackCalculate типа int function(float, bool, char*) нельзя присвоить i_CallbackPrint типа void function(void).