Указатели на функции (typedef)
MQL5 имеет ключевое слово typedef, которое позволяет описать специальный тип указателя на функцию.
В отличие от C++, где typedef имеет гораздо более широкое применение, в MQL5 typedef используется только для указателей на функции.
Синтаксис описания нового типа таков:
typedef function_result_type ( *function_type )( [list_of_input_parameters] ) ; |
Идентификатор function_type определяет имя типа, которое становится синонимом (алиасом) указателя на любую функцию, возвращающую значение заданного типа function_result_type и принимающую список входных параметров (list_of_input_parameters).
Например, у нас может быть 2 функции с одинаковыми прототипами (два входных параметра типа double и тип результата тоже double), выполняющие разные арифметические операции: сложение и вычитание (FuncTypedef.mq5).
double plus(double v1, double v2)
|
Их общий прототип легко описать для использования в качестве указателя:
typedef double (*Calc)(double, double); |
Данная запись вводит в программу тип Calc, с помощью которого можно определить переменную/параметр для хранения/передачи "ссылки" на любую функцию с таким прототипом, включая обе функции plus и minus. Указателем данный тип является потому, что в описании используется символ '*' (*Calc). Об особенностях "звездочки" в применении к указателям мы более подробно узнаем при изучении ООП.
Подобный класс указателей удобно использовать для создания настраиваемых алгоритмов, способных "на лету", в зависимости от входных данных, вызывать разные функции, соответствующие алиасу.
В частности, у нас есть возможность ввести обобщенную функцию-вычислитель:
double calculator(Calc ptr, double v1, double v2)
|
Её первый параметр описан с типом Calc. За счет этого мы можем передать в неё произвольную функцию с подходящим прототипом и в результате выполнить некую операцию, о сути которой сама функция calculator не знает. Она делает это путем делегирования вызова указателю: ptr(v1, v2). Поскольку ptr это указатель на функцию, данный синтаксис не только напоминает вызов функции, но и реально вызывает функцию, которую указатель хранит.
Обратите внимание, что мы предварительно проверяем параметр ptr на специальное значение NULL (NULL — эквивалент нуля для указателей). Дело в том, что указатель может никуда не указывать, то есть быть непроинициализированным. Так, в скрипте у нас описана глобальная переменная:
Calc calc; |
В ней как раз нет никакого указателя. Если бы не "защита" от NULL, вызов calculator с "пустым" указателем calc привел бы к ошибке времени исполнения "Вызов функции по некорректному указателю" ("Invalid function pointer call").
Вызовы функции calculator с разными указателями в первом параметре дадут следующие результаты (показаны в комментариях):
void OnStart()
|
Отметим, что все указатели на функции в отсутствие явной инициализации, заполняются нулевыми значениями. Это касается и глобальных, и локальных переменных данного типа.
Тип указателя, определенный с помощью typedef, можно возвращать из функций, например:
Calc generator(ushort type)
|
Кроме того, тип указателей на функции часто используется для функций обратного вызова (callback, см. FuncCallback.mq5). Представьте, что у нас есть функция DoMath, которая выполняет длительные вычисления (вполне вероятно, она реализована в отдельной библиотеке). С точки зрения удобства и дружественности пользовательского интерфейса было бы здорово показывать пользователю индикацию прогресса. Для этой цели можно определить специальный тип указателя на функцию для уведомлений о проценте выполненной работы (ProgressCallback), а в функцию DoMath добавить параметр этого типа. В коде DoMath следует периодически вызывать переданную функцию:
typedef void (*ProgressCallback)(const float percent);
|
Тогда вызывающий код может определить у себя требуемую функцию-callback, передать указатель на неё в DoMath и получать обновления по ходу вычислений.
void MyCallback(const float percent)
|
Указатели на функции работают только с пользовательскими функциями, определенными в MQL5. Они не могут указывать на встроенные функции MQL5 API.