Этот пост о том, как заставить интерпретатор lua вызывать функции написанные на С++.
Чтобы интерпретатор мог вызвать функцию, программа, сперва, должна эту функцию зарегистрировать.
lua_register(lua_State*, Имя функции, Адрес функции)
Функция должна возвращать значение типа int и получать в качестве параметра указатель на lua_State:
int glue_function(lua_State* L);
Склеивающая функция должна следовать определенному протоколу для нормальной работы. В первую очередь она должна получить через lua_State количество переданных ей аргументов, это значение находится на вершине стека во время вызова.
int glue_function(lua_State* L);
Затем функция может обращаться к элементам в стеке ( lua_to...(lua_State*, index) [вместо многоточия следует поставить нужный тип, например bool string и тд] ), имеющим индексы i >= 1 i <= count. Эти элементы могут иметь разные типы, функция должна проверять перед использованием типы переданных ей аргументов (функциями lua_is...(lua_State*, index) ), так как интерпретатор lua в момент вызова функции из кода lua не выполняет таких проверок (он не проверяет даже количество переданных в функцию аргументов, не то что их тип ). Чтобы возвратить значение, функция должна поместить в стэк возвращаемые значения (функции lua_push...) и возвратить их количество. Функция может вернуть множество значений (это называется кортеж), причем для функции lua абсолютно естественно, возвращать переменное число параметров.
Следующий пример иллюстрирует сказанное, программа регистрирует функцию print_arg, которая печатает количество переданных ей параметров, сами параметры и возвращает количество параметров. Скрипт использует эту функцию по назначению.
#include "stdafx.h"
extern "C" {
# include "lua.h"
# include "lauxlib.h"
# include "lualib.h"
}
#include <stdlib.h>
#include <stdio.h>
#include <string>
//Эта функция может принимать переменное число параметров через L
int stack_print (lua_State* L)
{
int n = lua_gettop(L);//получаем количество параметров переданных функции
printf("args: %d\n", lua_gettop(L) );
for (int i = 1; i <= n; ++i)
{
if (lua_isnumber(L, i))//проверяем тип i-го параметра
//если это вещественное число выводим его на экран
printf("result[%d] = %f\n", i, lua_tonumber(L, i));
else if (lua_isstring(L, i))//проверка на строку
printf("result[%d] = %s\n", i, lua_tostring(L, i));
else if (lua_istable(L, i))//если таблица
{ lua_pushnil(L);//кладем на вершину стека NULL
while ( lua_next(L, i) )//эта функция берет значение из стека (по индексу -1),
//и использует его в качестве ключа для поиска по таблице, которая
//находится в стеке по индексу i, в случае если находит, добавляет в
//стек пару ключ - значение, следующие в таблице после ключа находившегося в стеке
//если больше нет значений, ф-я возвращает 0
//lua_next используется для перебора элементов таблицы
{
/*
Состояние стека:
[-1] value <--Вершина стека
[-2] key
....
[i] table <-- текущий элемент
*/
//print key value
if (lua_type(L, -2) == LUA_TNUMBER)
{
printf ( "key = %f", lua_tonumber(L, -2) );
} else if ( lua_type(L, -2) )
{
printf ("key = %s", lua_tostring(L, -2));
}
//print value
if (lua_type(L, -1) == LUA_TNUMBER)
{
printf ( "\tvalue = %f\n", lua_tonumber(L, -1));
} else if ( lua_type(L, -1) )
{
printf ("\tvalue = %s\n", lua_tostring(L, -1));
}
//удаляем значение value из стэка, что-бы на следующей итерации
//ключ использовался для нахождения следующей пары значений из таблицы
lua_pop(L, 1);
/*
Состояние стека:
[-1] key <--Вершина стека
....
[i] table <-- текущий элемент
*/
}
}
else if (lua_isuserdata(L, i)) //пользовательский тип данных
{
void* udata = lua_touserdata(L, i);
printf("lua userdata %06", udata);
}
}
//возвпащаем один параметр - количество переданных значений
lua_pushinteger(L, (ptrdiff_t)n);
return 1;//сие означает что возвращается одно значение
}
int main(void)
{
int status, result;
lua_State *L;
L = lua_open();//Новая виртуальная машина луа
luaL_openlibs(L);//Открываем библиотеки base table string io math
lua_register(L,"print_arg", &stack_print);//Регистрируем функцию
status = luaL_loadfile(L, "script.lua");//загружаем файл
if (status) {
printf("file not found or syntax error\n");
return 1;
}
result = lua_pcall(L, 0, LUA_MULTRET, 0);//выполняем программу,
//точка входа в которую, расположена на вершине стека
if (result) {
printf("runtime error\n");
return 1;
}
lua_close(L); //Done
return 0;
}
код скрипта:
-- script.lua
x = 8
x = print_arg(5, x, 'lua')
io.write('x = '..x..'\n')
local table = {}
table['pi'] = 3.14159
table[3.14159] = 'pi'
print_arg(table)
Этот пример, уже намного более практичен. С помощю склеивающих функций вы можете изменять часть логики, не перекомпилируя проект, просто изменив скрипт. В следующей статье я покажу как реализовать в lua возможность, которая не поддерживается этим языком напрямую – объектно ориентированное программирование.
Комментариев нет:
Отправить комментарий