Native resources such as file handles, sockets, and XML parsers need deterministic cleanup even when exposed to Lua’s garbage-collected world. A common design is to store the native pointer in full userdata and give it a type-specific metatable.
For an Expat parser binding:
1 | typedef struct { |
Creation allocates userdata, initializes the native parser, and attaches the metatable. If native allocation fails, report a Lua error without leaving a partially initialized object exposed.
Idempotent close
Cleanup should be safe to call more than once:
1 | static int parser_close(lua_State *L) { |
Every other method must reject a closed handle before dereferencing it. Setting the pointer to NULL prevents a second call or the finalizer from freeing it twice.
Register methods and finalization
1 | static const luaL_Reg parser_methods[] = { |
Expose close() so callers can release scarce resources promptly. Treat __gc as a fallback because collection timing is nondeterministic. If callbacks retain Lua references, release their registry references during close and ensure native callbacks cannot fire after the state or userdata is gone.