luaanonymous-objects

Creation of anonymous userdata variables


I'm working with the lua C api to run scripts that interface with native clases. I'm taking advantage of Qt's MOC for the run time type information. So far, I've implemented the creation, deletion, and indexing of all the classes I want lua to be able to talk to.

Here's an example script of what I can do now:

myObject = MyClass.new()  --creates new userdata
otherObject = OtherClass.new()  --creates new userdata

myObject:functionForOthers(otherObject)  --method that takes userdata as argument

In that script, otherObject gets created and stays on the stack. It is then passed through functionForOthers() which accepts OtherClass objects as the argument.

But what if I didn't want otherObject to be put on the stack? What if I wanted it to be an anonymous variable where is exists only in the scope of the function call?

myObject = MyClass.new()

myObject:functionForOthers(OtherClass.new())

This still works, but the OtherObject instance gets created but never assigned to a variable leaving it on the stack unreachable until the scope ends. This doesn't cause any direct problems, but it bothers the memory-efficient part of me.

I have a lot of native code handling the backend of these operations, but the basic gist of it is that new is a field of global tables MyClass and OtherClass that points to a native CreateObject function. From there, an instance of the class is created and stored in a userdata after calling lua_newuserdata()

The functionForOthers() call makes use of the __index metamethod that points to a native IndexObject function which calls the method if it exists.

What is the basic approach to implementing anonymous userdata variables? I still want both scripts to be valid approaches, I just want a way to keep the second script's OtherClass object in scope until the function call is done.


Solution

  • It feels like you're misunderstanding some parts of Lua.

    Having some object passed as an argument to a function doesn't "leave" that object on stack. The stack only store a reference to the object. The other reference to the very same object is stored in otherObject variable. When you pass a variable as argument to functionForOthers() you get that reference copied by value to the stack, and that reference gets popped off the stack as soon as called function returns control.

    The object itself is stored on Lua heap. It will get destroyed/collected eventually, when garbage collector will find that there's no references left to that object. The object is not destroyed at the end of the scope, only reference is lost. And it's up to garbage collector when to actually delete the object.

    If you worry about the memory, you can call collectgarbage() periodically. That might stutter your program for some time though, so choose the right moment. Otherwise you can ignore it completely, just be sure you're not collecting references to objects in some long-living structure (global variables, registry, or your own dictionaries/caches). The references on stack are dropped quickly, unless you unwittingly create very deep or infinite recursive call. The object that can't be reached will be deleted automatically, that's the way mark-and-sweep garbage collector work.