-
- Функции
Работа с внешними DLL происходит через объект-обертку dll_wrapper. После создания объекта необходимо вызвать метод load с именем модуля DLL, который необходимо загрузить. В системе одновременно может быть загружен лишь один экземпляр любой DLL. Если загрузить DLL повторно, то будет использоваться библиотека загруженная в прошлый раз. Таким образом, можно загружать DLL в каждом из скриптов, и все они будут разделять доступ к одной DLL.
После того, как DLL загружена, ее функции вызываются с помощью вызова call(funcname, format, args). Он имеет следующие аргументы:
Пример использования:
static dll; function init() { dll = new_object("dll_wrapper"); dll.load("test.dll"); } function calc() { line[0] = dll.call("multiplication", "i8: i8, i8", 2, 3); }
Предположим, что содержимое файла test.cpp следующее:
// Пример кода на C++ __declspec(dllexport) int multiplication(int a, int b) { return a * b; }
И функция multiplication экспортируется.
Тогда линия line[0] будет иметь всюду значение 6.
Формат вызова функции (атрибут format) имеет следующий вид:
«соглашение_вызова возвращаемый_тип: аргумент1, аргумент2, …»
Соглашение вызова чаще всего не указывается и используется только для dll скомпилированных компиляторами Borland либо для соглашения вызова cdecl. Для компиляторов Borland требуется поставить префикс b, а для cdecl нижнее подчеркивание _ (при наличии обоих параметров они должны идти именно в указанном порядке). Пример:
dll_1.call("borland_function", "bi4: i4, i4"); dll_2.call("cdecl_function", "_i4: i4, i4"); dll_3.call("borland_cdecl_function", "b_i4: i4, i4"); dll_4.call("microsoft_stdcall_function", "i4: i4, i4");
В качестве типов аргументов и возвращаемого значения могут выступать четырех и восьмибайтовые целые и числа и числа с плавающей точкой, так же строки. Полный список аргументов:
На данный момент ATF не поддерживает указатели, а так же имеет сложную структуру внутренних типов и памяти, поэтому передать что-то по указателю на данный момент невозможно, это будет следующий этап развития объекта dll_wrapper (на данный момент он имеет больше тестовый вид).
Если функция не возвращает никакого значения, просто ничего не указывайте перед символом : (кроме соглашений вызова, если они требуются):
// Функция принимает строку в качестве // аргумента и ничего не возвращает dll.call("f1", ":s"); // Функция не возвращающая значение, // с соглашением вызова cdecl dll.call("f2", "_:i4, i4")
Если функция не возвращает значения и не принимает аргументов, то формат и аргументы не указываются вообще:
dll.call("hello");
ATF не имеет встроенного типа «указатель», а так же любые его данные не являются типизированными. Таким образом, например, вызвать следующую функцию С++ не удастся стандартными методами:
void f(int *p) { // некоторый код, пишущий по адресу p }
По этой же причине из ATF не удастся напрямую передать данные из массива в функцию dll.
Для того, чтобы решить эту проблему, в ATF 1.16 был введен объект нового типа raw_data, который имеет низкоуровневое внутреннее представление, совместимое со стандартными машинными типами. На данный момент типы данных поддерживаются следующие:
Объект типа raw_data поддерживает три функции:
Задает внутреннее представление объекта. В качестве типа указывается строка содержащее одно их значений выше. В качестве number указывается для скольки именно экземпляров такого типа необходимо выделить память. Например, если вы ожидаете, что функция dll fn запишет по передаваемому ей указателю массив из 100 чисел двойной точности, вы должны предварительно инициализировать объект raw_data следующим образом:
var data = new_object("raw_data"); data.define("double", 100); dll.call("fn", ":p", data);
Функция get() возвращает либо массив данных, содержащихся в raw_data, либо одно значение, если оно было выделено лишь одно. Таким образом можно получить данные, которые были записаны функцией dll по указателю. Пример выше можно дополнить, например, следующим образом:
var data = new_object("raw_data"); data.define("double", 100); dll.call("fn", ":p", data); var result = data.get(); var i = 0; while (i < 100) { line[0][i] = result[i]; i += 1; }
Функция set() инициализирует объект данными из переменной data. Если data - скалярная переменная, то в raw_data будет выделена память под один элемент. Если data - массив, то будет выделено памяти ровно под количество элементов, содержащихся в массиве. Используется функция set() аналогично
var data = new_object("raw_data"); var array = new_object("array"); fillArraySomehow(array); data.set("int4", array); dll.call("fn", ":p", data);
Скрипты выполняются последовательно, так что проблем с синхронизацией функций DLL возникать не должно. В тоже время обработка событий окружения или таймера может исполняться в отдельном потоке, поэтому можно быть уверенным, что никакая функция DLL не будет вызвана одновременно из разных потоков Transaq, однако нельзя закладываться на последовательность вызова функций:
function onEnvEvent(var string) { dll.call("onEnvEventFunc"); } function onNewCandle() { dll.call("onNewCandleFunc1") dll.call("onNewCandleFunc2"); }
В этом примере последовательность вызовов функций DLL может быть следующей: onNewCandleFunc1, onEnvEventFunc, onNewCandleFunc2.