#ifndef LUATB_TYPED_STACK_H #define LUATB_TYPED_STACK_H #include #include #include #include "is_callable.h" #include "core/CoreIncludes.h" /** * @brief Represents a typed version of the lua stack * * We need a separate class for this because we need to specialize the functions * and that's not possible if we didn't specialize the class. And the logical * separation makes sense as well. */ class LuaTBTypedStack { public: /** * @brief Get a non-function argument from the lua stack and convert it to type 'T' * @param lua Pointer to the lua state * @return Top element of the lua stack with the appropriate type * * Note: Pops the value from the stack. */ template static typename std::enable_if::value, T>::type getArgument(lua_State *lua) { T value = LuaTBTypedStack::getFromLuaStack(lua); lua_pop(lua, 1); return value; } /** * @brief Get a function-type argument from the lua stack and convert it to type 'T' * @param lua Pointer to the lua state * @return Top element of the lua stack with the appropriate type * * Specialization if 'T' is a callable type (std::function). Saves the lua * function in the registry and constructs a function to call it again. * * Note: Pops the value from the stack. */ template static typename std::enable_if::value, T>::type getArgument(lua_State *lua) { // Here's the tricky part. Apparently, references in the registry can only // be called once. This is why we always have to re-add it to the registry // after every call. That means, we need a persistent variable for 'ref' and // that's only possible with a pointer in this case. int *ref = new int(luaL_ref(lua, LUA_REGISTRYINDEX)); LuaTBTypedStack::callbackRefs.push_back(std::unique_ptr(ref)); // 'decltype(&T::operator())' represents the function signature of the callable object. return GetLuaCallback::value(lua, ref); } private: template struct GetLuaCallback; // Gets a value from the top of the lua stack and returns it with the proper type. // Does not pop the value! /** * @brief Get a value from the lua stack and convert it to type 'T' * @param lua Pointer to the lua state * @return Top element of the lua stack with the appropriate type * * Note: Does NOT pop the value from the stack. */ template static T getFromLuaStack(lua_State *lua); // This class is only valid for std::function types. The type argument will not // be a normal function signature because it is the signature of // std::function<>::operator(), which is also the reason for the 'const'. /** * @brief Needed to get a lambda to call a lua function * * We need this class, becasue we need to specify the signature of the function * before. */ template struct GetLuaCallback::*)(Args...) const> { /** * @brief Get a lambda to call a lua function later * @param lua Pointer to the lua state * @param ref Pointer to the lua registry reference where the function is stored * @return Lambda that will call the function specified with ref */ static std::function value(lua_State *lua, int *ref) { // We can't return 'callLuaFunction' directly, because we need the // additional arguments 'lua' and 'ref' which we would like to hide // from the user. return [lua, ref](Args... args){callLuaFunction(lua, ref, args...);}; } }; /** * @brief Pushes all arguments to the lua stack and calls a lua function afterwards * @param lua Pointer to the lua state * @param ref Pointer to the lua registry reference where the function is stored * @param args Arguments for the function */ template static void callLuaFunction(lua_State *lua, int *ref, Args... args) { // Get the value of the callback from the registry and push it to the stack lua_rawgeti(lua, LUA_REGISTRYINDEX, *ref); // Duplicate it on the stack so we can save it in the registry again after // we called the callback. lua_pushvalue(lua, 1); // We pass one extra argument in case the function has no arguments at all pushArgumentsToLuaStack(lua, args..., false); lua_pcall(lua, sizeof...(args), 0, 0); // Release the old reference and save the function in the registry again lua_unref(lua, *ref); *ref = luaL_ref(lua, LUA_REGISTRYINDEX); } /** * @brief Pushes nothing onto the stack * * This is needed in case the function has no arguments at all. Otherwise, we * would have a missing argument for such a function. This is also why we pass * a dummy argument in 'callLuaFunction', so we have at least one argument. It * is the termination point for the recursive template. */ template static void pushArgumentsToLuaStack(lua_State *, T) { } /** * @brief Recursively pushes arguments to the lua stack * @param lua Pointer to the lua state * @param first First argument to push onto the stack * @param args The remaining arguments to push onto the stack */ template static void pushArgumentsToLuaStack(lua_State *lua, First first, Args... args) { pushToLuaStack(lua, first); pushArgumentsToLuaStack(lua, args...); } /** * @brief Pushes a value to the lua stack * @param lua Pointer to the lua state * @param value The value to push * * Note: Only the specializations are valid */ template static void pushToLuaStack(lua_State *lua, T value); static std::list > callbackRefs; }; #endif // LUATB_TYPED_STACK_H