Подключение библиотек и #import функций
Импорт функций осуществляется из откомпилированных модулей MQL5 (файлы *.ex5) и из модулей динамических библиотек Windows (файлы *.dll). Имя модуля указывается в директиве #import, после чего следуют описания прототипов импортируемых функций. Такой блок должен заканчиваться еще одной директивой #import, причем она может быть без имени и просто закрывать собой блок, либо в директиве может быть указано имя другой библиотеки, и тем самым одновременно начинается следующей блок импорта. В конце серии блоков импорта всегда должна идти директива без имени библиотеки.
В простейшем случае директива выглядит следующим образом:
#import "[путь⌡ имя_модуля [.расширение]"
|
Имя файла библиотеки можно указывать без расширения: тогда по умолчанию предполагается dll-библиотека. Расширение ex5 указывать обязательно.
Перед именем может идти путь размещения библиотеки. По умолчанию, если пути нет, библиотеки ищутся в папке MQL5/Libraries или в папке рядом с MQL-программой, куда подключена библиотека. В противном случае для поиска библиотек применяются разные правила в зависимости от типа — DLL или EX5. Эти правила освещены в отдельном разделе.
Вот пример последовательных блоков импорта из двух библиотек:
#import "user32.dll"
|
При наличии таких директив импортируемые функции можно вызывать из исходного кода точно так же, как и функции, определенные непосредственно в самой MQL-программе. Все технические "сложности" с загрузкой библиотек и переадресации вызовов в сторонние модули берет на себя среда исполнения MQL-программ.
Для того чтобы компилятор мог правильно оформить вызов импортируемой функции и организовать передачу параметров, необходимо полное описание — с типом результата, со всеми параметрами, модификаторами и значениями по умолчанию, если они присутствуют в источнике.
Так как импортируемые функции находятся вне компилируемого модуля, компилятор не может проверить правильность передаваемых параметров и возвращаемых значений. Любое несовпадение формата ожидаемых и получаемых данных приведет к ошибке во время выполнения программы, причем это может проявляться как критическая остановка программы, так и непредвиденное поведение.
В случае если библиотека не смогла загрузиться или вызываемая импортируемая функция не была найдена, MQL-программа останавливает свою работу с соответствующим сообщением в журнале. Программа не сможет запускаться, пока проблема не будет решена, например, путем модификации и перекомпиляции, размещения искомой библиотеки в одном из мест по пути поиска или разрешением на использование DLL (только для DLL-библиотек).
При совместном использовании нескольких библиотек (не важно — DLL или EX5) следует помнить, что они должны иметь разные имена вне зависимости от каталогов их размещения. Все импортируемые функции получают область видимости, совпадающую с именем файла библиотеки, то есть это своего рода пространство имен, неявно выделяемое под каждую подключенную библиотеку.
Импортируемые функции могут иметь любые имена, в том числе, совпадающие с именами встроенных функций (хотя это не рекомендуется). Более того можно одновременно импортировать из разных модулей функции с одинаковыми именами. В подобных случаях следует применять операцию разрешения контекста для определения того, какая из функций должна вызываться.
Например:
#import "kernel32.dll"
|
Покажем простой пример скрипта LibRandTest.mq5, использующего функции из EX5-библиотеки, созданной в предыдущем разделе.
#include <MQL5Book/LibRand.mqh> |
Во входных параметрах можно выбрать количество элементов в массиве чисел, параметры распределения, а также шаг гистограммы, которую мы посчитаем, чтобы убедиться в приблизительном соответствии распределения нормальному закону.
input int N = 10000;
|
Инициализация встроенного в MQL5 генератора случайных чисел (равномерного распределения) производится значением RandomSeed или, если здесь оставлен 0, берется GetTickCount (новое при каждом запуске).
Для построения гистограммы используем MapArray и QuickSortStructT (мы с ними уже работали в разделах о мультивалютных индикаторах и о сортировке массивов, соответственно). В карте будут накапливаться счетчики попадания случайных чисел в ячейки гистограммы с шагом HistogramStep.
#include <MQL5Book/MapArray.mqh>
|
Для отображения гистограммы на основе карты нужно уметь сортировать карту в порядке значений-ключей. Для этого пришлось определить производный класс.
#define COMMA ,
|
Обратите внимание, что макрос COMMA становится альтернативным представлением символа запятой ',' и используется при вызове другого макроса SORT_STRUCT. Если бы не эта подстановка, запятая внутри пары Pair<K,V> трактовалась бы препроцессором, как обычный разделитель параметров макроса, в результате чего на входе SORT_STRUCT получилось бы 4 параметра вместо ожидаемых 3-х — это вызвало бы ошибку компиляции. Препроцессор ничего не знает о синтаксисе MQL5.
В начале OnStart, после инициализации генератора, проверим получение одиночной случайной строки и массива строк разной длины.
void OnStart()
|
Далее тестируем нормально-распределенные случайные числа.
// вызываем еще одну библиотечную функцию: PseudoNormalArray
|
Вот какой результат получился при запуске с настройками по умолчанию (рандомизация таймером — каждый запуск будет выбирать новый seed).
Random seed: 8859858 Random HEX-string: E58B125BCCDA67ABAB2F1C6D6EC677 Random strings: "K4ZOpdIy5yxq4ble2" "NxTrVRl6q5j3Hr2FY" "6qxRdDzjp3WNA8xV" "UlOPYinnGd36" "6OCmde6rvErGB3wG" Random pseudo-gaussian histogram: -9.50 ( 2) -8.50 ( 1) -8.00 ( 1) -7.00 ( 1) -6.50 ( 5) -6.00 ( 10) * -5.50 ( 10) * -5.00 ( 24) * -4.50 ( 28) ** -4.00 ( 50) *** -3.50 ( 100) ****** -3.00 ( 195) *********** -2.50 ( 272) *************** -2.00 ( 510) **************************** -1.50 ( 751) ****************************************** -1.00 (1029) ********************************************************* -0.50 (1288) ************************************************************************ +0.00 (1457) ********************************************************************************* +0.50 (1263) ********************************************************************** +1.00 (1060) *********************************************************** +1.50 ( 772) ******************************************* +2.00 ( 480) *************************** +2.50 ( 280) **************** +3.00 ( 172) ********** +3.50 ( 112) ****** +4.00 ( 52) *** +4.50 ( 43) ** +5.00 ( 10) * +5.50 ( 8) +6.00 ( 8) +6.50 ( 2) +7.00 ( 3) +7.50 ( 1) |
В данной библиотеке мы экспортировали и импортировали только функции со встроенными типами. Однако гораздо интереснее и более востребованы с практической точки зрения объектные интерфейсы со структурами, классами и шаблонами. Про нюансы их применения в библиотеках мы поговорим в отдельном разделе.
При тестировании экспертов и индикаторов в тестере следует иметь в виду важный момент, связанный с библиотеками. Библиотеки, необходимые для основной тестируемой MQL-программы определяются автоматически из директив #import. Однако если из основной программы вызывается пользовательский индикатор, к которому подключена какая-либо библиотека, то необходимо в явном виде указать в свойствах программы, что она опосредованно зависит от конкретной библиотеки. Это делается с помощью директивы:
#property tester_library "путь_имя_библиотеки.расширение"