c++memory-managementlualua-userdata

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.


Solution

  • 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;
    }