Sometimes, when implementing an API, it's convenient to create an object which is a "singleton" with respect to the lua State, and to be able to easily refer to this object. Usually you want to do this by creating a unique key for this object inside the lua registry.
When the object is a lua value, primer provides a simple way to do this
using primer::push_singleton. When the object is a
C++ object external to lua, you can use primer::registry_helper.
It is fairly common in the lua API to want to create some private object hidden in the registry to support some feature or hold some implementation detail.
Often times, people make up string constants to use as the registry key, and create a "lazy construction" function which first checks the registry for a pre-cached value, and otherwise computes it.
Primer gives a very terse and convenient way to achieve this:
namespace primer { template <void (*)(lua_State *)> void push_singleton(lua_State *); template <int (*)(lua_State *)> void push_singleton(lua_State *); } // end namespace primer
The idea is, the function which creates the value is the template parameter, and the template function ensures the lazy construction aspect.
The registry key is simply the function pointer corresponding to the producer function itself.
Here's an example producer function
void my_table(lua_State * L) { lua_newtable(L); lua_pushinteger(L, 5); lua_setfield(L, -2, "a"); lua_pushinteger(L, 7); lua_setfield(L, -2, "b"); }
The function simply pushes a table of some kind onto the stack. Now, the call
primer::push_singleton<&my_table>(L)
will push our table onto the stack, lazily constructing it if necessary.
It sometimes happens that we have an object which we know will outlive lua, and we would like to store a pointer to it in the registry and recover it easily. Especially, this happens when the object is a member of an API object.
We can store and retrieve such objects in the following way:
void primer::registry_helper<T>::store_self(L, T *); T * primer::registry_helper<T>::obtain_self(L);
Clearly, this only works if this is the unique object of this type which will be registered.
Note that obtain_self has
UB if no object of this type was previously stored. If PRIMER_DEBUG
is defined, then it will cause an assertion failure.
A different approach is to simply make the object a feature of the API,
or just a member variable of the API object. Then it will be in scope of
any callback function that you are defining, or can be accessed by any
features that need to access it. The registry_helper
technique should probably only be used when that's not suitable for some
reason.