Как передать пользовательские данные из одного фрагмента Lua в другой в Stack Overflow

Я пытаюсь получить userdata из сценария Lua (chunk A) в C ++ (через возвращенную переменную из функции в моем примере), а затем, передайте это userdata вернуться к сценарию Lua (chunk B) из C ++ (через аргумент функции в моем примере), поэтому userdata может быть использован в chunk B как это было в chunk A,

MyBindings.h

class Vec2
{
public:
Vec2():x(0), y(0){};
Vec2(float x, float y):x(x), y(y){};
float x, y;
};

MyBindings.i

%module my
%{
#include "MyBindings.h"%}

%include "MyBindings.h"

main.cpp

#include <iostream>
#include <lua.hpp>

extern "C"{
int luaopen_my(lua_State *L);
}

int main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaopen_my(L);
lua_settop(L, 0);
/* chunk A */
luaL_dostring(L, "local vec2 = my.Vec2(3, 4)\n""function setup()\n""return vec2\n""end\n");
/* chunk B */
luaL_dostring(L, "function test(p)\n""print(p.x)\n""end\n");
void *userDataPtr = nullptr;

/* call setup function */
int top = lua_gettop(L);
lua_getglobal(L, "setup");
if (lua_pcall(L, 0, LUA_MULTRET, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
/* check the return value */
if (lua_gettop(L) - top)
{
/* store userdata to a pointer */
if (lua_isuserdata(L, -1))
userDataPtr = lua_touserdata(L, -1);
}
/* check if userDataPtr is valid */
if (userDataPtr != nullptr)
{
/* call test function */
lua_getglobal(L, "test");
lua_pushlightuserdata(L, userDataPtr); /* pass userdata as an argument */
if (lua_pcall(L, 1, 0, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
}
lua_close(L);
}

Результат, который я получаю:

[строка «local vec2 = my.Vec2 (3, 4) …»]: 6: попытка проиндексировать
Значение userdata (локальное ‘p’)

Результат, который я ожидаю:

3

Можно ли получить userdata от chunk A а затем передать это chunk B так что его можно использовать как в chunk A?

1

Решение

В дополнение к другому ответу я хотел бы показать вариант, где вы храните ссылку на значение в реестре Lua. Преимущество этого подхода заключается в том, что вам не нужно сохранять значение в стеке и думать о том, каким будет смещение. Смотрите также 27.3.2 — Ссылки в «Программирование в Lua».

Этот подход использует три функции:

  1. int luaL_ref (lua_State *L, int t);

    Извлекает из стека самое верхнее значение, сохраняет его в таблице по индексу t и возвращает индекс значения в этой таблице. Следовательно, чтобы сохранить значение в реестре мы используем

    userDataRef = luaL_ref(L, LUA_REGISTRYINDEX);
    
  2. int lua_rawgeti (lua_State *L, int index, lua_Integer n);

    Помещает в стек значение элемента n стола в index (t[n] в Луа). Следовательно, чтобы получить значение по индексу userDataRef из реестра мы используем

    lua_rawgeti(L, LUA_REGISTRYINDEX, userDataRef);
    
  3. void luaL_unref (lua_State *L, int t, int ref);

    Удаляет ссылку, хранящуюся в индексе ref в таблице на t такой, что ссылка может быть сборщиком мусора и индекс ref можно использовать повторно. Следовательно, чтобы удалить ссылку userDataRef из реестра мы используем

    luaL_unref(L, LUA_REGISTRYINDEX, userDataRef);
    
#include <iostream>
#include <lua.hpp>

extern "C" {
int luaopen_my(lua_State *L);
}

int main() {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaopen_my(L);
lua_settop(L, 0);
/* chunk A */
luaL_dostring(L, "local vec2 = my.Vec2(3, 4)\n""function setup()\n""return vec2\n""end\n");
/* chunk B */
luaL_dostring(L, "function test(p)\n""print(p.x)\n""end\n");
int userDataRef = LUA_NOREF;

/* call setup function */
int top = lua_gettop(L);
lua_getglobal(L, "setup");
if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
/* check the return value */
if (lua_gettop(L) - top) {
/* store userdata to a pointer */
userDataRef = luaL_ref(L, LUA_REGISTRYINDEX);
}

/* check if userDataRef is valid */
if (userDataRef != LUA_NOREF && userDataRef != LUA_REFNIL) {
/* call test function */
lua_getglobal(L, "test");
lua_rawgeti(L, LUA_REGISTRYINDEX, userDataRef);

/* free the registry slot (if you are done) */
luaL_unref(L, LUA_REGISTRYINDEX, userDataRef);

if (lua_pcall(L, 1, 0, 0)) {
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
}
}
lua_close(L);
}

Может быть, вы хотите проверить Sol2 обертка для Lua-C-API. Он может делать именно то, что вы хотите с минимальным шаблоном. Однако для этого требуется C ++ 14.

#include <iostream>

#define SOL_CHECK_ARGUMENTS 1
#include <sol.hpp>

extern "C" int luaopen_my(lua_State *L);

int main() {
sol::state L;
L.open_libraries();
luaopen_my(L);

/* chunk A */
L.script("local vec2 = my.Vec2(3, 4)\n""function setup()\n""return vec2\n""end\n");
/* chunk B */
L.script("function test(p)\n""print(p.x)\n""end\n");

auto userDataRef = L["setup"]();
L["test"](userDataRef);
}
2

Другие решения

Вы теряете всю информацию о типе объекта, когда получаете необработанный указатель на данные userdata и выдвигаете его в аргументы как lightuserdata. Lightuserdata даже не имеет отдельных метатаблиц.

Правильный способ — передать значение Lua как есть. Оставьте исходное возвращенное значение в стеке Lua или скопируйте его в другой контейнер Lua (ваша таблица Lua для временных таблиц или реестр Lua), затем скопируйте это значение в стек Lua, чтобы передать его в качестве аргумента. Таким образом, вам не нужно ничего знать о реализации связывания. Вам даже не нужно беспокоиться, если это пользовательские данные или любой другой тип Lua.

Исходя из вашего кода, это может выглядеть так:

#include <iostream>
#include <lua.hpp>

extern "C"{
int luaopen_my(lua_State *L);
}

int main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);

/* chunk A */
luaL_dostring(L, "local vec2 = {x=3, y=4}\n""function setup()\n""return vec2\n""end\n");
/* chunk B */
luaL_dostring(L, "function test(p)\n""print(p.x)\n""end\n");

/* call setup function */
int top = lua_gettop(L);
lua_getglobal(L, "setup");

if (lua_pcall(L, 0, LUA_MULTRET, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);

exit(EXIT_FAILURE); // simpy fail for demo
}

/* check the return value */
if (lua_gettop(L) - top)
{
// the top now contains the value returned from setup()

/* call test function */
lua_getglobal(L, "test");

// copy the original value as argument
lua_pushvalue(L, -2);

if (lua_pcall(L, 1, 0, 0))
{
std::cout << lua_tostring(L, -1) << '\n';
lua_pop(L, 1);
exit(EXIT_FAILURE);
}

// drop the original value
lua_pop(L, 1);

}else
{
// nothing is returned, nothing to do
}
lua_close(L);
}
6

По вопросам рекламы [email protected]