Working With lua::reference
March 13, 2026 ยท View on GitHub
lua::reference is Luacxx's registry-backed handle for keeping a Lua value
alive while C++ holds onto it.
It is especially useful when:
- you need to save a Lua callback inside a C++ object
- you want to keep a Lua table around between calls
- you need a Lua value to survive after it leaves the stack
Unlike lua::index, a lua::reference is not just a view of a current stack
slot. It stores the value in the Lua registry, which prevents Lua's garbage
collector from reclaiming it while the reference exists.
Why lua::reference Exists
Stack-based objects like lua::index are convenient, but they are temporary.
Once the stack changes, the value at that slot may change too.
lua::reference solves a different problem: "I need to keep this Lua value
around in C++ and use it later."
Typical examples include:
- storing a Lua function for later callbacks
- caching a Lua table in a C++ subsystem
- returning a Lua value from one part of your binding layer and using it later
Basic Construction
You can create a reference from a state, a stack position, or another Lua-facing value:
lua::reference empty(state);
lua::reference from_stack(state, 1);
lua::reference from_global(lua::global(state, "package"));
Copying a lua::reference creates another registry reference to the same Lua
value. Each lua::reference owns its own registry slot and releases it in its
destructor.
Saving A Lua Callback
This is one of the most common uses:
struct Handler
{
lua::reference callback;
};
int set_callback(lua_State* state)
{
auto handler = lua::get<Handler*>(state, 1);
handler->callback = lua::index(state, 2);
return 0;
}
void fire(lua_State* state, Handler& handler)
{
lua::call(handler.callback, 42);
}
The important part is that handler.callback keeps the Lua function alive even
after the original stack slot is gone.
Reading Through A Reference
lua::reference supports the same style of typed access as other Luacxx
objects:
std::string name = ref.get<std::string>();
int value = lua::get<int>(ref);
It also supports operator[], which means you can navigate tables from a
saved reference:
lua::reference package(lua::global(state, "package"));
std::string path = package["path"].get<std::string>();
Assigning To A Reference
Assigning to a lua::reference replaces the value stored in its registry slot:
lua::reference ref(state);
ref = 42;
ref = lua::global(state, "package");
ref = lua::index(state, 2);
Assigning nil releases the current registry value:
ref = lua::value::nil;
Internally, Store<lua::reference> treats nil as "no referenced value" and
returns the reference to the empty state.
Lifetime And Ownership
lua::reference is RAII-managed.
When a reference is destroyed, it calls luaL_unref(...) and releases its
registry slot.
That means:
- the referenced Lua value stays alive while the
lua::referenceexists - once the last relevant reference is gone, Lua can collect the value normally
The reference does not copy the Lua value into C++. It keeps Lua ownership, but pins the value in the registry.
Empty References
A default-constructed or released lua::reference behaves like an empty
handle:
lua::reference ref;
if (!ref) {
// no referenced Lua value
}
An empty reference pushes nil when treated as a Lua value.
How It Relates To Other Luacxx Types
lua::indexis a stack slot viewlua::globalis a named global lookuplua::linkis a keyed lookup from another sourcelua::referenceis a persistent registry-backed handle
If you only need to inspect a value already on the stack, lua::index is
usually enough.
If you need the value to survive after the current call or stack frame,
lua::reference is the right tool.
Common Pattern: Return A Persistent Lua Value
Because lua::reference is a normal Luacxx conversion target, you can return
it from helpers that call Lua:
lua::reference callable = lua::global(state, "make_handler")();
lua::call(callable, "hello");
This is often nicer than manually managing a stack result and immediately converting it into a registry reference yourself.