Tom Tom - 3 months ago 31
C++ Question

Lifetime of Lua userdata pointers

If I create a userdata object and stash it in a table, then get a reference to it in C/C++, for how long is that reference valid? Is the reference in C/C++ guaranteed to be valid for as long as the userdata is held in the table in Lua? Or is there a risk that the Lua runtime will move the userdata object, invalidating the C/C++ reference to it?

Here's what I'm doing:

// Initially, the stack contains a table
class Foo { ... };
lua_pushstring(L, "my_userdata");
void* mem = lua_newuserdata(L, sizeof(Foo));
new (mem) Foo();
lua_settable(L, -3);

// Later:
lua_pushstring(L, "my_userdata");
lua_gettable(L, -2);
Foo *foo = (Foo*)lua_touserdata(L, -1);
lua_pop(L, 1);
// How long will this pointer be valid?


Am I better off using
operator new
and a light userdata here?

Answer

The reference (or pointer since Lua is written in C) will remain valid for the lifetime of the userdata.

Lua's chief architect addressed this on the Lua-l mailing list:

Quote: Apr 18, 2006; Roberto Ierusalimschy

The caution is about strings, not about userdata (although we actually did not say that explicitly in the manual). We have no intention of allowing userdata addresses to change during GC. Unlike strings, which are an internal data in Lua, the only purpose of userdata is to be used by C code, which prefer that things stay where they are :)

You can control the lifetime of a userdata by anchoring it in the state:

There are several reasons you may prefer a full userdata over a lightuserdata:

  • Full userdata may have its own uservalue and metatable (all lightuserdata shares the same metatable)
  • Finalization though the __gc metamethod
  • Several convenience API functions for working with userdata (luaL_newmetatable, luaL_setmetatable, etc.)

A common way of creating userdata from a class in C++ is to use the pointer-to-pointer idiom:

class Foo { ... };

static int new_Foo(lua_State *L) {
    // begin userdata lifetime
    Foo **ud = static_cast<Foo **>(lua_newuserdata(L, sizeof *ud)); 
    luaL_setmetatable(L, "Foo");

    // begin C++ object lifetime  
    *ud = new Foo();
    return 1;
}

// __gc metamethod
static int delete_Foo(lua_State *L) {  
    Foo **ud = static_cast<Foo **>(luaL_checkudata(L, 1, "Foo"));

    // end C++ object lifetime
    delete *ud;

    // end userdata lifetime
    return 0;
}
Comments