Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How free up memory allocated by lua_newuserdata with delete operator?

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.

like image 621
user3312225 Avatar asked Feb 15 '14 02:02

user3312225


2 Answers

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;
}
like image 135
Alex Avatar answered Nov 15 '22 09:11

Alex


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.

like image 37
Jeffrey Hill Avatar answered Nov 15 '22 09:11

Jeffrey Hill