Question:
I have an object in c++ code like
TestObj Test();
I would like to pass not only the Lua stack to the function that will be called from the Lua script, but also a reference to this object. Those. instead of:
int lua_getOneString(lua_State *L)
{
lua_pushstring(L, "Hello");
return 1; //Количество возвращаемых аргументов
}
I want to do something like this (this is to avoid making the TestObj object global):
int lua_getOneString(lua_State *L, TestObj & obj)
{
lua_pushstring(L, obj.pushStringToLua());
return 1; //Количество возвращаемых аргументов
}
I can’t find information about this, and if you do this, the compiler swears:
note: initializing argument 2 of 'void lua_pushcclosure(lua_State*, lua_CFunction, int)' LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
Answer:
all functions called from lua
must have the same signature: int (lua_State *)
– so your function must be converted to this form. There are two options: make your TestObj
static (which is a good option for a factory for example), or get it from the lua
stack as a parameter. I suspect that you are interested in the second option (in any case, the first option is trivial).
In order to get your TestObj
from the lua
stack you first need to push it there – but how do you do that? lua
has a special userdata
type that can be used to represent objects from c
code. It can be a simple pointer or some amount of memory allocated right on the lua
stack. In the second case, the interpreter will free the memory it occupied, which is quite good if your object consists of basic types, like int
, float
, fixed-size c
-arrays, etc. which do not require a destructor call. If the destructor must be called (for example, when you use new
or use containers that allocate memory on the heap), then you also have to write a finalizer.
Let's show some code:
/**\brief push new TestObj to lua stack
*/
int lua_new_obj(lua_State *state) {
void *addr = lua_newuserdata(state, sizeof(TestObj));
TestObj *obj = new (addr) TestObj{};
// XXX also you can use light userdata
// TestObj *obj = new TestObj{};
// lua_pushlightuserdata(state, obj);
return 1;
}
/**\brief get TestObj from lua stack and do something
*/
int lua_foo(lua_State *state) {
void *addr = lua_touserdata(state, 1);
TestObj *obj = reinterpret_cast<TestObj *>(addr);
obj->doSomething();
return 0;
}
/**\brief call desctructor TestObj
* \note needed only if you use lightuserdata, or if fou your TestObj destructor must be called
*/
int lua_finilize(lua_State *state) {
void *addr = lua_touserdata(state, 1);
TestObj *obj = reinterpret_cast<TestObj *>(addr);
obj->~TestObj();
return 0;
// XXX if you use lightuserdata then you need free memory
// delete obj; // destructor calls automaticly
}
This code is not perfect, as there are two obvious problems: you need to manually call the finalizer (in the case lightuserdata
or if you need to call the destructor on the object) and check the input parameters (if you pass any other usedata
to your function, then at best your program will immediately crash , at worst – will not fall). But these problems have a solution – metatables! Yes, for userdata
, as well as for tables, you can set your own metatables, but that's another story.