Взаимодействие происходит по стандартному алгоритму: в скрипте QLua(Lua) вызываются функции из подключенной при помощи директивы "require" библиотеки DLL, написанной на языке C/C++ (о том как ее создать, можно прочитать в данной статье). В функцию можно передавать данные, в виде параметров. Обрабатывать их внутри функции и получать в скрипт результат(ы) выполнения функции.
Для этого служит Lua-стек, который представляет из себя массив разнотипных данных (таблицу Lua). Первое значение, помещенное в стек, получает индекс 1, второе - 2 и т.д.
Для каждого отдельного вызова функции автоматически используется отдельный стек!
Получить результаты можно обычным способом, и, если их несколько, используя параллельное присваивание.
Пример, если бы функция из приведенного примера DLL возвращала 4 значения:
require("QluaCSharpConnector"); -- Получение значений в QLua a,b,c,d = QluaCSharpConnector.TestFunc(); -- Передать значения в функцию на C/C++ можно обычным способом QluaCSharpConnector.TestFunc(true, 1, 2.5, "Текст"); |
Для того, чтобы получить значения внутри функции DLL, переданные из скрипта, в Lua существует несколько функций, приведу основные:
lua_gettop(L); //Вернет индекс последнего элемента в стеке. Т.к. индексы начинаются с 1, то это значение, так же, означает количество элементов в стеке. По-умолчанию, максимально возможный индекс установлен в 8000, но может быть изменен в файле luaconf.h lua_settop(L, i); //Меняет размер стека, устанавливая количество элементов равным i, если i больше, чем на данный момент элементов в стеке, то функция добавляет в стек недостающие элементы, устанавливая их значения как nil. Если i меньше, чем на данный момент элементов в стеке, то функция удаляет из стека лишние элементы. Если передать i равное 0, то функция очистит стек. lua_toboolean(L, i); //Возьмет из стека значение по индексу i (индексы, как и всегда в Lua, начинаются с 1) и преобразует его к типу boolean, вернет 1 для всех значений, кроме false и nil, иначе вернет 0. Так же, вернет 0, если указан несуществующий индекс. lua_isboolean(L, i); //Проверяет, является-ли значение в стеке по данному индексу типом boolean. lua_tointeger(L, i); //Возьмет из стека значение по индексу i, и попытается преобразовать его к типу int. Вернет полученное число, либо 0. Результат преобразования не целого числа непредсказуем. lua_tonumber(L, i); //Возьмет из стека значение по индексу i, и попытается преобразовать его к типу double. Вернет полученное число, либо 0. lua_isnumber(L, i); //Проверяет, является-ли значение в стеке по данному индексу числом. lua_tostring(L, i); //Возьмет из стека значение по индексу i, и попытается преобразовать его к типу string. Вернет полученную строку, либо NULL, если элемент стека не являлся ни строкой, ни числом. Значение в стеке, так же, изменится на тип string!!! lua_isstring(L, i); //Проверяет, является-ли значение в стеке по данному индексу типом string. |
Пример получения значений внутри функции C/C++ с предварительной проверкой на тип, переданных в примере выше:
static int forLua_TestFunc(lua_State *L) { boolean B; int I; double D; const char *S; // Получает из стека переданные скриптом данные if (lua_isboolean(L, 1)) B = lua_toboolean(L, 1); if (lua_isnumber(L, 2)) I = lua_tointeger(L, 2); if (lua_isnumber(L, 3)) D = lua_tonumber(L, 3); if (lua_isstring(L, 4)) S = lua_tostring(L, 4); return(0); } |
Основные функции для добавления элементов в стек:
lua_pushboolean(L, B); //Добавляет в стек элемент типа boolean lua_pushinteger(L, B); //Добавляет в стек элемент типа int lua_pushnumber(L, B); //Добавляет в стек элемент типа double lua_pushstring(L, B); //Добавляет в стек элемент типа const char* |
Пример с добавлением отправки полученных значений обратно в скрипт:
static int forLua_TestFunc(lua_State *L) { boolean B; int I; double D; const char *S; // Получает из стека переданные скриптом данные if (lua_isboolean(L, 1)) B = lua_toboolean(L, 1); if (lua_isnumber(L, 2)) I = lua_tointeger(L, 2); if (lua_isnumber(L, 3)) D = lua_tonumber(L, 3); if (lua_isstring(L, 4)) S = lua_tostring(L, 4); // Очищает стек Lua lua_settop(L, 0); // Добавляет в стек полученные ранее значения lua_pushboolean(L, B); lua_pushinteger(L, I); lua_pushnumber(L, D); lua_pushstring(L, S); // Возвращает значения в скрипт return(1,2,3,4); } |
Скрипт, демонстрирующий работу данной функции:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | require("QluaCSharpConnector"); IsRun = true; function main() while IsRun do sleep(1000); end; end; function OnStop() -- Отправляет в DLL и получает обратно значения b,i,d,s = QluaCSharpConnector.TestFunc(true, 1, 2.5, "Текст"); -- Выводит полученные значения в сообщении message(tostring(b).." "..tostring(i).." "..tostring(d).." "..s); IsRun = false; end; |
Так же, из QLua(Lua) в DLL(C/C++) можно передавать МАССИВЫ (таблицы Lua).
Ниже приведен пример DLL с функцией forLua_SendArray, которая получает из QLua массив чисел, вычисляет сумму значений и отправляет результат в C#:
1 2 3 4 5 6 | require("QluaCSharpConnector"); function main() Array = {1,2,3,4,5}; QluaCSharpConnector.SendArray(Array); end; |
Разные функции:
lua_getglobal(L, name); //Ищет глобальную переменную с заданным именем и помещает на вершину стека |
Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!
Подскажите пожалуйста пример, как на C++ получить строку записанную в именованную память, и что немаловажно синхронизировать это.
Мне необходимо в стороннем приложении на С++ получать данные о сделках которые поступают в функцию OnTrade в Quik LUA.
DLL для получения и записи данных из OnTrade в именованную память по примеру в данной статье я сделал, а как эти данные получать на C++ и получать именно тогда когда они поступили (синхронно) не знаю 🙁 Помогите пожалуйста. Я без примера не разберусь так как новичок в этом деле.
А возможно ли отправить из c++ массив в lua?
Кто-нибудь использовал CLR или иные способы вывода форм из DLL? На данный момент пробую использовать CLR, но похоже, что из-за этого DLL после остановке LUA скрипта (где подключен DLL) - не выгружается.