Типобезопасные обратные вызовы в 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).