An L_Reg_sequence is a
sequence of named functions.
It is quite common in lua C API programming to need to create lists of functions paired with names. lua has a standard idiom for this. The following structure is defined in the lua headers:
struct luaL_Reg { const char * name; lua_CFunction func; };
Many lua api calls (e.g. lua_register) take as a parameter
a pointer const luaL_Reg
*, which is expected to point into
an array, terminated by a luaL_Reg
holding two null pointers.
It is common to need to make such lists when creating APIs, or specifying userdata.
In primer, we respect this idiom, but we extend it as follows:
luaL_Reg is supported
std::array,
std::initializer_list, std::vector)
is supported, with or without null terminators. It needs to satisfy
the conditions for use in a range-based for loop.
luaL_Reg
-- as long as it has public members const
char *
name and lua_CFunction
func, that's enough
In total we call this concept "L_Reg_sequence".
This code pushes a collection of functions to lua and gives them names in the global environment.
lua C API:
lua_pushcfunction(L, &foo); lua_setglobal(L, "foo"); lua_pushcfunction(L, &bar); lua_setglobal(L, "bar"); lua_pushcfunction(L, &baz); lua_setglobal(L, "baz");
A different way using lua C API:
static const luaL_Reg funcs[] = { {"foo", &foo}, {"bar", &bar}, {"baz", &baz}, {nullptr, nullptr} }; lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); luaL_setfuncs(L, funcs); lua_pop(L, 1);
Using primer:
static const std::vector<luaL_Reg> funcs{ {"foo", &foo}, {"bar", &bar}, {"baz", &baz}, {nullptr, nullptr} }; lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); primer::set_funcs(L, funcs); lua_pop(L, 1);
A different way using primer:
static constexpr std::array<luaL_Reg, 3> funcs() { return {{"foo", &foo}, {"bar", &bar}, {"baz", &baz}}; } lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); primer::set_funcs(L, funcs); lua_pop(L, 1);
The concept is used by an internal function detail::iterate_L_Reg_sequence:
// Iterate over an L_Reg_sequence template <typename T, typename F> void iterate_L_Reg_sequence(T && t, F && f) { static_assert(is_L_Reg_sequence<decay_t<T>>::value, "Expected an L_Reg_sequence"); for (const auto & reg : is_L_Reg_sequence<decay_t<T>>::adapt(std::forward<T>(t))) { if (reg.name || reg.func) { std::forward<F>(f)(reg.name, reg.func); } } }
which calls a functor using each name, func
pair from the sequence.
primer::set_funcs and a few variations are as
follows:
There are a few variations of primer::set_funcs.
primer::set_funcs is analogous to luaL_setfuncs, it assumes there is
a table on top of the stack and registers each function with it, using
the name as a key (see documentation).
primer::set_funcs_reverse is the same, but
the function is the key and the name is the value.
primer::set_funcs_prefix is the same, except
a prefix string is prepended to each name.
primer::set_funcs_prefix_reverse is analogous.
template <typename T> void set_funcs(lua_State * L, T && seq) { PRIMER_ASSERT_STACK_NEUTRAL(L); PRIMER_ASSERT_TABLE(L); detail::iterate_L_Reg_sequence(std::forward<T>(seq), [&](const char * name, lua_CFunction func) { if (name && func) { lua_pushcfunction(L, func); lua_setfield(L, -2, name); } }); } template <typename T> void set_funcs_reverse(lua_State * L, T && seq) { PRIMER_ASSERT_STACK_NEUTRAL(L); PRIMER_ASSERT_TABLE(L); detail::iterate_L_Reg_sequence(std::forward<T>(seq), [&](const char * name, lua_CFunction func) { if (name && func) { lua_pushcfunction(L, func); lua_pushstring(L, name); lua_settable(L, -3); } }); } template <typename T> void set_funcs_prefix(lua_State * L, const std::string & prefix, T && seq) { PRIMER_ASSERT_STACK_NEUTRAL(L); PRIMER_ASSERT_TABLE(L); detail::iterate_L_Reg_sequence( std::forward<T>(seq), [&](const char * name, lua_CFunction func) { if (name && func) { lua_pushcfunction(L, func); lua_setfield(L, -2, (prefix + name).c_str()); } }); } template <typename T> void set_funcs_prefix_reverse(lua_State * L, const std::string & prefix, T && seq) { PRIMER_ASSERT_STACK_NEUTRAL(L); PRIMER_ASSERT_TABLE(L); detail::iterate_L_Reg_sequence(std::forward<T>(seq), [&](const char * name, lua_CFunction func) { if (name && func) { lua_pushcfunction(L, func); lua_pushstring(L, (prefix + name).c_str()); lua_settable(L, -3); } }); }