
Взаимодействие происходит по стандартному алгоритму: в скрипте 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#:
[su_spoiler title="Код DLL(С/С++), обрабатывающей массив" style="fancy"]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | #include <windows.h> //=== Необходимые для Lua константы ============================================================================// #define LUA_LIB #define LUA_BUILD_AS_DLL //=== Заголовочные файлы LUA ===================================================================================// extern "C" { #include "Lua\lauxlib.h" #include "Lua\lua.h" } //=== Получает указатель на выделенную именованную память =====================================================// // Имя для выделенной памяти TCHAR Name[] = TEXT("MyMemory"); // Создаст, или подключится к уже созданной памяти с таким именем HANDLE hFileMapMyMemory = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 256, Name); //=== Стандартная точка входа для DLL ==========================================================================// BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } //=== Реализация функций, вызываемых из LUA ====================================================================// static int forLua_SendArray(lua_State *L) // Получает массив из QLua, вычисляет сумму значений, отправляет сумму в C# { // Если указатель на память получен if (hFileMapMyMemory) { // Получает доступ (представление) непосредственно к чтению/записи байт PBYTE pb = (PBYTE)(MapViewOfFile(hFileMapMyMemory, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 256)); // Если доступ получен if (pb != NULL) { // Если запись пустая (либо первая отправка, либо C# очистил память, подтвердив получение) if (pb[0] == 0) { // Проверяет, является-ли первый элемент стека массивом (таблицей Lua) if (lua_istable(L, 1)) { int ArraySum = 0; lua_pushnil(L); //Первый ключ //Функция lua_next перебирает все пары "ключ"-"значение" в таблице, //вторым параметром указывается индекс в стеке, по которому расположен массив (таблица Lua) while (lua_next(L, 1) != 0) { // в паре "ключ" находится по индексу -2, "значение" находится по индексу -1 lua_tonumber(L, -2); // Так можно получить числовое значение ключа ArraySum = ArraySum + lua_tonumber(L, -1); // Считает сумму значений массива // освобождает стек для следующей итерации lua_pop(L, 1); } char Str[10] = ""; // массив символов для строкового представления суммы значений itoa(ArraySum, Str, 10); // конвертирует число в строку memcpy(pb, Str, strlen(Str)); // записывает строку в именованную память } } // Закрывает представление UnmapViewOfFile(pb); // Закрывает указатель на память CloseHandle(hFileMapMyMemory); } } return(0); } //=== Регистрация реализованных в dll функций, чтобы они стали "видимы" для Lua ================================// static struct luaL_reg ls_lib[] = { { "SendArray", forLua_SendArray }, { NULL, NULL } }; //=== Регистрация названия библиотеки, видимого в скрипте Lua ==================================================// extern "C" LUALIB_API int luaopen_QluaCSharpConnector(lua_State *L) { luaL_openlib(L, "QluaCSharpConnector", ls_lib, 0); return 0; } |
[/su_spoiler]
Если данную DLL назвать QluaCSharpConnector.dll и запустить нижеприведенный скрипт, то в именованной памяти "MyMemory" будет строка "15", которую можно обработать в приложении 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); //Ищет глобальную переменную с заданным именем и помещает на вершину стека |
Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!