Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/SuperOrxoBros_HS18/SuperOrxoBros_HS18/src/orxonox/scriptablecontroller/luatb_typed_stack.h @ 12175

Last change on this file since 12175 was 12175, checked in by siramesh, 5 years ago

Super Orxo Bros (Sidharth Ramesh, Nisa Balta, Jeff Ren)

File size: 6.1 KB
Line 
1#ifndef LUATB_TYPED_STACK_H
2#define LUATB_TYPED_STACK_H
3
4#include <type_traits>
5#include <functional>
6#include <lua.hpp>
7#include "is_callable.h"
8#include "core/CoreIncludes.h"
9
10
11/**
12 * @brief Represents a typed version of the lua stack
13 *
14 * We need a separate class for this because we need to specialize the functions
15 * and that's not possible if we didn't specialize the class. And the logical
16 * separation makes sense as well.
17 */
18class LuaTBTypedStack
19{
20public:
21
22    /**
23     * @brief Get a non-function argument from the lua stack and convert it to type 'T'
24     * @param lua Pointer to the lua state
25     * @return Top element of the lua stack with the appropriate type
26     *
27     * Note: Pops the value from the stack.
28     */
29    template<typename T>
30    static typename std::enable_if<!IsCallable<T>::value, T>::type getArgument(lua_State *lua)
31    {
32        T value = LuaTBTypedStack::getFromLuaStack<T>(lua);
33        lua_pop(lua, 1);
34        return value;
35    }
36
37    /**
38     * @brief Get a function-type argument from the lua stack and convert it to type 'T'
39     * @param lua Pointer to the lua state
40     * @return Top element of the lua stack with the appropriate type
41     *
42     * Specialization if 'T' is a callable type (std::function). Saves the lua
43     * function in the registry and constructs a function to call it again.
44     *
45     * Note: Pops the value from the stack.
46     */
47    template<typename T>
48    static typename std::enable_if<IsCallable<T>::value, T>::type getArgument(lua_State *lua)
49    {
50        // Here's the tricky part. Apparently, references in the registry can only
51        // be called once. This is why we always have to re-add it to the registry
52        // after every call. That means, we need a persistent variable for 'ref' and
53        // that's only possible with a pointer in this case.
54        int *ref = new int(luaL_ref(lua, LUA_REGISTRYINDEX));
55        LuaTBTypedStack::callbackRefs.push_back(std::unique_ptr<int>(ref));
56
57        // 'decltype(&T::operator())' represents the function signature of the callable object.
58        return GetLuaCallback<decltype(&T::operator())>::value(lua, ref);
59    }
60
61private:
62    template<typename T> struct GetLuaCallback;
63
64    // Gets a value from the top of the lua stack and returns it with the proper type.
65    // Does not pop the value!
66    /**
67     * @brief Get a value from the lua stack and convert it to type 'T'
68     * @param lua Pointer to the lua state
69     * @return Top element of the lua stack with the appropriate type
70     *
71     * Note: Does NOT pop the value from the stack.
72     */
73    template<typename T> static T getFromLuaStack(lua_State *lua);
74
75    // This class is only valid for std::function types. The type argument will not
76    // be a normal function signature because it is the signature of
77    // std::function<>::operator(), which is also the reason for the 'const'.
78    /**
79     * @brief Needed to get a lambda to call a lua function
80     *
81     * We need this class, becasue we need to specify the signature of the function
82     * before.
83     */
84    template<typename Ret, typename... Args>
85    struct GetLuaCallback<void (std::function<Ret(Args...)>::*)(Args...) const>
86    {
87        /**
88         * @brief Get a lambda to call a lua function later
89         * @param lua Pointer to the lua state
90         * @param ref Pointer to the lua registry reference where the function is stored
91         * @return Lambda that will call the function specified with ref
92         */
93        static std::function<void (Args...)> value(lua_State *lua, int *ref)
94        {
95            // We can't return 'callLuaFunction' directly, because we need the
96            // additional arguments 'lua' and 'ref' which we would like to hide
97            // from the user.
98            return [lua, ref](Args... args){callLuaFunction<Ret, Args...>(lua, ref, args...);};
99        }
100    };
101
102    /**
103     * @brief Pushes all arguments to the lua stack and calls a lua function afterwards
104     * @param lua Pointer to the lua state
105     * @param ref Pointer to the lua registry reference where the function is stored
106     * @param args Arguments for the function
107     */
108    template<typename Ret, typename... Args>
109    static void callLuaFunction(lua_State *lua, int *ref, Args... args)
110    {
111        // Get the value of the callback from the registry and push it to the stack
112        lua_rawgeti(lua, LUA_REGISTRYINDEX, *ref);
113
114        // Duplicate it on the stack so we can save it in the registry again after
115        // we called the callback.
116        lua_pushvalue(lua, 1);
117
118        // We pass one extra argument in case the function has no arguments at all
119        pushArgumentsToLuaStack<Args...>(lua, args..., false);
120        lua_pcall(lua, sizeof...(args), 0, 0);
121
122        // Release the old reference and save the function in the registry again
123        lua_unref(lua, *ref);
124        *ref = luaL_ref(lua, LUA_REGISTRYINDEX);
125    }
126
127    /**
128     * @brief Pushes nothing onto the stack
129     *
130     * This is needed in case the function has no arguments at all. Otherwise, we
131     * would have a missing argument for such a function. This is also why we pass
132     * a dummy argument in 'callLuaFunction', so we have at least one argument. It
133     * is the termination point for the recursive template.
134     */
135    template<typename T>
136    static void pushArgumentsToLuaStack(lua_State *, T)
137    { }
138
139    /**
140     * @brief Recursively pushes arguments to the lua stack
141     * @param lua Pointer to the lua state
142     * @param first First argument to push onto the stack
143     * @param args The remaining arguments to push onto the stack
144     */
145    template<typename First, typename... Args>
146    static void pushArgumentsToLuaStack(lua_State *lua, First first, Args... args)
147    {
148        pushToLuaStack(lua, first);
149        pushArgumentsToLuaStack<Args...>(lua, args...);
150    }
151
152    /**
153     * @brief Pushes a value to the lua stack
154     * @param lua Pointer to the lua state
155     * @param value The value to push
156     *
157     * Note: Only the specializations are valid
158     */
159    template<typename T>
160    static void pushToLuaStack(lua_State *lua, T value);
161
162    static std::list<std::unique_ptr<int> > callbackRefs;
163};
164
165#endif // LUATB_TYPED_STACK_H
Note: See TracBrowser for help on using the repository browser.