I'm trying to get userdata from a Lua script(chunk A) in C++(through a returned variable from function in my example) and then, later pass this userdata back to Lua script(chunk B) from C++(through a function argument in my example) so the userdata can be used in chunk B as it was in the 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);
}
The Result I get :
[string "local vec2 = my.Vec2(3, 4)..."]:6: attempt to index a userdata value (local 'p')
The Result I expect :
3
Is it possible to get userdata from chunk A and then pass this to chunk B so it can be used like it was in chunk A?
In addition to the other answer I would like to show a variant where you store the a reference to the value in the Lua registry. The advantage of this approach is that you don't have to keep the value on the stack and think about what the offset will be. See also 27.3.2 – References in “Programming in Lua”.
This approach uses three functions:
int luaL_ref (lua_State *L, int t);Pops from the stack the uppermost value, stores it into the table at index
tand returns the index the value has in that table. Hence to save a value in the registry we useint lua_rawgeti (lua_State *L, int index, lua_Integer n);Pushes onto the stack the value of the element
nof the table atindex(t[n]in Lua). Hence to retrieve a value at indexuserDataReffrom the registry we usevoid luaL_unref (lua_State *L, int t, int ref);Removes the reference stored at index
refin the table attsuch that the reference can be garbage collected and the indexrefcan be reused. Hence to remove the referenceuserDataReffrom the registry we useMaybe you want to check out the Sol2 wrapper for the Lua-C-API. It can do exactly what you want with minimal boilerplate. However, it requires C++14.