Взаимодействие происходит по стандартному алгоритму: в скрипте 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, "Текст"); |
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. |
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);
} |
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* |
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);
} |
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; |
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#:
Код DLL(С/С++), обрабатывающей массив
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;
} |
#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;
}
Если данную
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; |
require("QluaCSharpConnector");
function main()
Array = {1,2,3,4,5};
QluaCSharpConnector.SendArray(Array);
end;
Разные функции:
lua_getglobal(L, name); //Ищет глобальную переменную с заданным именем и помещает на вершину стека |
lua_getglobal(L, name); //Ищет глобальную переменную с заданным именем и помещает на вершину стека
Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!