How can I free memory allocated by lua_newuserdata
?
I have a class called Foo
, and this class has a constructor and desstructor, and I need execute both, but I don't know how to use the C++ operator delete
, because I didn't use new
to allocate memory.
I tried do that in the Lua function new that create the object:
Foo *pf = reinterpret_cast<Foo *>(
lua_newuserdata(L, sizeof(Foo)));
and at gc function I tried that:
Foo *foo = reinterpret_cast<Foo *>(lua_touserdata(L, 1));
delete foo;
But I got a segmentation fault.
In this case you need to use a lua concept called userdatum, it means, you need to allocate a pointer to your object using lua_newuserdata.
To allocate memory do something like that:
Foo **pfoo = reinterpret_cast<Foo **>(lua_newuserdata(L, sizeof(Foo*)));
*pfoo = new Foo(foo);
and at the garbage collector function you can do that:
Foo **foo = reinterpret_cast<Foo **>(lua_touserdata(L, 1));
delete *foo;
Below is a complete code example using the concept of userdatum
#define FOO "foo"
class Foo {
public:
Foo(const char *name) {
this->name = (char *) malloc(strlen(name) + 1);
strncpy(this->name, name, strlen(name));
}
Foo(const Foo &obj) {
this->name = (char *) malloc(strlen(name) + 1);
strncpy(this->name, obj.name, strlen(obj.name));
}
const char* get_name() const {
return this->name;
}
~Foo() {
free(this->name);
}
private:
char *name;
};
static Foo* push_foo(lua_State *L, Foo foo) {
Foo **pfoo = reinterpret_cast<Foo **>(
lua_newuserdata(L, sizeof(Foo*)));
*pfoo = new Foo(foo);
luaL_getmetatable(L, FOO);
lua_setmetatable(L, -2);
return *pfoo;
}
static Foo* chk_foo(lua_State *L, int index) {
Foo *foo;
luaL_checktype(L, index, LUA_TUSERDATA);
foo = *reinterpret_cast<Foo **>(luaL_checkudata(L, index, FOO));
if (foo == NULL)
luaL_error(L, "error");
return foo;
}
static int foo_new(lua_State *L) {
int argc = lua_gettop(L);
if(argc != 1)
luaL_error(L, "string argument expected");
const char* str = luaL_checkstring(L, 1);
push_foo(L, Foo(str));
luaL_getmetatable(L, FOO);
lua_setmetatable(L, -2);
std::cout << "Lua object created!" << std::endl;
return 1;
}
static int foo_get(lua_State *L) {
Foo *foo = chk_foo(L, 1);
luaL_argcheck(L, foo != NULL, 1, "Error foo");
lua_pushstring(L, foo->get_name());
return 1;
}
static int foo_gc(lua_State *L) {
Foo **foo = reinterpret_cast<Foo **>(lua_touserdata(L, 1));
luaL_argcheck(L, *foo != NULL, 1, "Error foo");
delete *foo;
std::cout << "Lua GC executed!" << std::endl;
return 0;
}
int luaopen_foolib(lua_State *L) {
static const luaL_Reg Obj_lib[] = {
{ "get", &foo_get },
{ NULL, NULL }
};
static const luaL_Reg LuaLib_Foo[] = {
{ "new", &foo_new },
{ NULL, NULL }
};
luaL_newlib(L, LuaLib_Foo);
// Stack: MyLib
luaL_newmetatable(L, FOO);
luaL_newlib(L, Obj_lib);
lua_setfield(L, -2, "__index");
lua_pushstring(L, "__gc");
lua_pushcfunction(L, foo_gc);
lua_settable(L, -3);
lua_pop(L, 1);
return 1;
}
int main(int argc, char **argv) {
lua_State *L;
L = luaL_newstate();
luaL_openlibs(L);
luaL_requiref(L, "foo", &luaopen_foolib, 1);
lua_pop(L, 1);
const char *code = "f = foo.new(\"my_test\")\nprint(f:get())";
if(luaL_loadstring(L, code) != 0)
{
std::cout << "Could not load: " << argv[1] << std::endl;
exit(EXIT_FAILURE);
}
if(lua_pcall(L, 0, 0, 0) != 0)
{
std::cout << "Error: " << lua_tostring(L, -1) << std::endl;
lua_pop(L, 1);
exit(EXIT_FAILURE);
}
lua_close(L);
return 0;
}
There are two steps when C++ deletes an object, running the destructor and freeing up the dynamically allocated memory. If lua_newuserdata is called to allocate storage for the object then the placement new can be used to run the constructor using a Lua allocated memory space, and a Lua garbage collection "__gc" method can be used to explicitly call the object's destructor, for example "pMyObject->~MyClass ()". However, storage reclamation (freeing up the lua_newuserdata dynamically allocated memory) is done automagically by the Lua environment and therefore calling delete in the "__gc" method will cause your software to fail. If the memory is allocated by Lua then it should also be consistently released by Lua.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With