[Orxonox-commit 6898] r11519 - in code/branches/ScriptableController_HS17/src/orxonox: . scriptablecontroller

kohlia at orxonox.net kohlia at orxonox.net
Mon Oct 30 16:06:11 CET 2017


Author: kohlia
Date: 2017-10-30 16:06:11 +0100 (Mon, 30 Oct 2017)
New Revision: 11519

Added:
   code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/
   code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/CMakeLists.txt
   code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/is_callable.h
   code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.h
   code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.ipp
   code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.cc
   code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.h
   code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.cc
   code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.h
   code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.cc
   code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.h
Log:
See previous


Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/CMakeLists.txt
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/CMakeLists.txt	                        (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/CMakeLists.txt	2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,6 @@
+ADD_SOURCE_FILES(ORXONOX_SRC_FILES
+  luatb_typed_stack.cc
+  scriptable_controller.cc
+  scriptable_controller_api.cc
+)
+

Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/is_callable.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/is_callable.h	                        (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/is_callable.h	2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,40 @@
+#ifndef IS_CALLABLE_H
+#define IS_CALLABLE_H
+
+#include <type_traits>
+
+// See https://stackoverflow.com/a/15396757/7421666
+template<typename T>
+struct IsCallableImpl
+{
+private:
+    typedef char(&yes)[1];
+    typedef char(&no)[2];
+
+    struct Fallback { void operator()(); };
+    struct Derived : T, Fallback { };
+
+    template<typename U, U> struct Check;
+
+    template<typename>
+    static yes test(...);
+
+    template<typename C>
+    static no test(Check<void (Fallback::*)(), &C::operator()>*);
+
+public:
+    static const bool value = sizeof(test<Derived>(0)) == sizeof(yes);
+};
+
+// Checks if a type is callable (has an 'operator()'). This will not work for
+// normal functions without modifications, but we don't need that case anyway.
+template<typename T>
+struct IsCallable
+    : std::conditional<
+        std::is_class<T>::value,
+        IsCallableImpl<T>,
+        std::false_type
+    >::type
+{ };
+
+#endif // IS_CALLABLE_H

Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.h	                        (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.h	2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,26 @@
+#ifndef LUATB_H
+#define LUATB_H
+
+#include <string>
+
+struct lua_State;
+
+// Makes certain functions visible to lua while staying type-safe on the
+// C++ side.
+template<typename ThisType, typename FunctionType>
+class LuaTB
+{
+public:
+    // Make a function visible to lua. If you want to make a function 'foo'
+    // visible, you should call it like this:
+    //
+    // LuaWrapper<decltype(foo)>::registerFunction<foo>( ... );
+    template<FunctionType func>
+    static void registerFunction(ThisType *_this, lua_State *lua, std::string name);
+};
+
+// We need to include all type-dependant template implementations, because the
+// compiler needs to know for which types it has to instantiate those
+#include "luatb.ipp"
+
+#endif // LUATB_H

Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.ipp
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.ipp	                        (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb.ipp	2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,57 @@
+
+#include <lua.hpp>
+#include <iostream>
+#include <type_traits>
+#include "scriptable_controller_api.h"
+#include "luatb_typed_stack.h"
+
+
+// TODO Make free functions work
+
+// This class is only valid for function types. As a neat side-effect, having
+// this specialization in the .ipp file, also hides all of the private members
+// from the user (kind of).
+template<typename ThisType, typename Ret, typename... Args>
+class LuaTB<ThisType, Ret (ThisType::*)(Args...)>
+{
+public:
+    template<Ret (ThisType::*func)(Args...)>
+    static void registerFunction(ThisType *_this, lua_State *lua, std::string name)
+    {
+        // Store the 'this' pointer of the caller in the extraspace
+//        *static_cast<ThisType**>(lua_getextraspace(lua)) = _this;
+
+        // Make a function visible to lua that will call 'func' with the correct
+        // arguments
+        lua_register(lua, name.c_str(), toLuaSignature<func>);
+    }
+
+private:
+    // Represents a function that can made visible to lua with the correct
+    // signature. It will call the corresponding C++ function with the
+    // correctly typed arguments
+    template<Ret (ThisType::*func)(Args...)>
+    static int toLuaSignature(lua_State *lua)
+    {
+        // The number of arguments is the first item on the stack
+        int argc = lua_tointeger(lua, lua_gettop(lua));
+        lua_pop(lua, 1);
+
+        // Check if the number of arguments match
+        if(argc != sizeof...(Args))
+        {
+            std::cerr << "ERROR: LuaTB: Lua script called a function with wrong number of arguments" << std::endl;
+            return LUA_ERRSYNTAX;
+        }
+
+        // Retrieve 'this' pointer of caller
+//        ThisType *_this = *static_cast<ThisType**>(lua_getextraspace(lua));
+ThisType *_this = orxonox::ScriptableControllerAPI::this_;
+
+        // Call getArgument for every argument seperately to convert each argument
+        // to the correct type and call the function afterwards
+        ((*_this).*func)( (LuaTBTypedStack::getArgument<Args>(lua))... );
+
+        return 0;
+    }
+};

Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.cc
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.cc	                        (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.cc	2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,29 @@
+
+#include "luatb_typed_stack.h"
+#include <string>
+
+// Explicit and full specializations need to be in a .cpp file
+
+template<>
+int LuaTBTypedStack::popFromLuaStack<int>(lua_State *lua)
+{ return lua_tointeger(lua, lua_gettop(lua)); }
+
+template<>
+double LuaTBTypedStack::popFromLuaStack<double>(lua_State *lua)
+{ return lua_tonumber(lua, lua_gettop(lua)); }
+
+template<>
+std::string LuaTBTypedStack::popFromLuaStack<std::string>(lua_State *lua)
+{ return lua_tostring(lua, lua_gettop(lua)); }
+
+template<>
+void LuaTBTypedStack::pushToLuaStack<int>(lua_State *lua, int value)
+{ lua_pushinteger(lua, value); }
+
+template<>
+void LuaTBTypedStack::pushToLuaStack<double>(lua_State *lua, double value)
+{ lua_pushnumber(lua, value); }
+
+template<>
+void LuaTBTypedStack::pushToLuaStack<std::string>(lua_State *lua, std::string value)
+{ lua_pushstring(lua, value.c_str()); }

Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.h	                        (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/luatb_typed_stack.h	2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,92 @@
+#ifndef LUATB_TYPED_STACK_H
+#define LUATB_TYPED_STACK_H
+
+#include <type_traits>
+#include <functional>
+#include <lua.hpp>
+#include "is_callable.h"
+
+// 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:
+    // Get an argument from the lua stack and convert it to type 'T'
+    template<typename T>
+    static T getArgument(lua_State *lua)
+    {
+        T value = LuaTBTypedStack::popFromLuaStack<T>(lua);
+        lua_pop(lua, 1);
+        return value;
+    }
+
+private:
+    // Gets the top element of the lua stack and converts it to the appropriate
+    // type. Only works for certain types, if 'T' is none of these types, you'll
+    // get an error.
+    template<typename T>
+    static typename std::enable_if<!IsCallable<T>::value, T>::type popFromLuaStack(lua_State *lua);
+
+    // Specialization if 'T' is a callable type (std::function). Saves the lua
+    // function in the registry and constructs a function to call it again.
+    // 'decltype(&T::operator())' represents the function signature of the callable
+    // object.
+    template<typename T>
+    static typename std::enable_if<IsCallable<T>::value, T>::type popFromLuaStack(lua_State *lua)
+    {
+        return GetLuaCallback<decltype(&T::operator())>::value(lua, luaL_ref(lua, LUA_REGISTRYINDEX));
+    }
+
+    // Get a function that will call a lua callback
+    template<typename T> struct GetLuaCallback;
+
+    // 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'.
+    template<typename Ret, typename... Args>
+    struct GetLuaCallback<Ret(std::function<Ret(Args...)>::*)(Args...) const>
+    {
+        // Returns a lambda that will push the arguments to the lua stack and call
+        // the function afterwards. We can't return 'callLuaFunction' directly,
+        // because we need the additional arguments 'lua' and 'ref' which we would
+        // like to hide from the user.
+        static std::function<Ret (Args...)> value(lua_State *lua, int ref)
+        {
+            return [lua, ref](Args... args){return callLuaFunction<Ret, Args...>(lua, ref, args...);};
+        }
+    };
+
+    // Pushes all arguments to the lua stack and calls a lua function afterwards
+    template<typename Ret, typename... Args>
+    static Ret callLuaFunction(lua_State *lua, int ref, Args... args)
+    {
+        lua_rawgeti(lua, LUA_REGISTRYINDEX, ref);
+        // We pass one extra argument in case the function has no arguments at all
+        pushArgumentsToLuaStack<Args...>(lua, args..., false);
+        lua_pcall(lua, 1, sizeof...(args), 0);
+
+        // TODO
+        return Ret();
+    }
+    // This is needed in case the function has no arguments at all. Otherwise, we
+    // would have a missing argument for such function. This is also why we pass
+    // a dummy argument in 'callLuaFunction', so we have at least one argument.
+    template<typename T>
+    static void pushArgumentsToLuaStack(lua_State *, T)
+    { }
+
+    // Recursively pushes arguments to the lua stack
+    template<typename First, typename... Args>
+    static void pushArgumentsToLuaStack(lua_State *lua, First first, Args... args)
+    {
+        pushToLuaStack(lua, first);
+        pushArgumentsToLuaStack<Args...>(lua, args...);
+    }
+
+    // Pushes a value to the lua stack. Only the specializations are valid.
+    template<typename T>
+    static void pushToLuaStack(lua_State *lua, T value);
+};
+
+#endif // LUATB_TYPED_STACK_H

Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.cc
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.cc	                        (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.cc	2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,83 @@
+
+#include "scriptable_controller.h"
+#include "luatb.h"
+
+namespace orxonox
+{
+
+// Used https://csl.name/post/lua-and-cpp/ as a reference
+int ScriptableController::runScript(const std::string &file_path)
+{
+    int ret;
+
+    // Create a lua object
+    lua_State *lua = luaL_newstate();
+    if(lua == nullptr)
+    {
+        orxout(internal_warning) << "ScriptableController: Falied to load script " << file_path << std::endl;
+        return LUA_ERRMEM;
+    }
+
+    // Make standard libraries available in the Lua object
+    luaL_openlibs(lua);
+
+    // Register the API
+    ScriptableControllerAPI *api = new ScriptableControllerAPI(lua, this);
+
+    // Load the program; this supports both source code and bytecode files.
+    if((ret = luaL_loadfile(lua, file_path.c_str())) != 0)
+    {
+        this->printLuaError(lua);
+        delete api;
+        return ret;
+    }
+
+    // Execute the script
+    if((ret = lua_pcall(lua, 0, LUA_MULTRET, 0)) != 0)
+    {
+        this->printLuaError(lua);
+        delete api;
+        return ret;
+    }
+
+    // Remeber the api
+    this->apis_.push_back(std::unique_ptr<ScriptableControllerAPI>(api));
+
+    // Only the caller knows, when the end-of-life of the script is reached,
+    // so we give him control over when to destroy it.
+    return 0;
+}
+
+void ScriptableController::registerWorldEntity(int id, WorldEntity *obj)
+{
+    this->worldEntities_[id] = obj;
+}
+
+void ScriptableController::registerControllableEntity(int id, ControllableEntity *obj)
+{
+    this->worldEntities_[id] = obj;
+}
+
+WorldEntity *ScriptableController::getWorldEntityByID(int id) const
+{
+    auto obj = this->worldEntities_.find(id);
+    return obj != this->worldEntities_.end() ? obj->second : nullptr;
+}
+
+ControllableEntity *ScriptableController::getControllableEntityByID(int id) const
+{
+    auto obj = this->controllabelEntities_.find(id);
+    return obj != this->controllabelEntities_.end() ? obj->second : nullptr;
+}
+
+void ScriptableController::printLuaError(lua_State *lua)
+{
+    // The error message is on top of the stack.
+    // Fetch it, print it and then pop it off the stack.
+    // Yes, this is 'const char*' and not 'std::string' because that's what lua gives us.
+    const char* message = lua_tostring(lua, -1);
+    std::cout << message << std::endl;
+    lua_pop(lua, 1);
+}
+
+}

Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.h	                        (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller.h	2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,41 @@
+#ifndef SCRIPTABLE_CONTROLLER_H
+#define SCRIPTABLE_CONTROLLER_H
+
+#include <lua.hpp>
+#include <string>
+#include <list>
+#include <map>
+#include <memory>
+#include "scriptable_controller_api.h"
+#include "core/CoreIncludes.h"
+#include "worldentities/WorldEntity.h"
+#include "worldentities/ControllableEntity.h"
+
+struct lua_State;
+
+namespace orxonox
+{
+
+class ScriptableController
+{
+public:
+    int runScript(const std::string &file_path);
+
+    void registerWorldEntity(int id, WorldEntity *obj);
+    void registerControllableEntity(int id, ControllableEntity *obj);
+
+    WorldEntity *getWorldEntityByID(int id) const;
+    ControllableEntity *getControllableEntityByID(int id) const;
+
+private:
+    std::list<std::unique_ptr<ScriptableControllerAPI> > apis_;
+    ControllableEntity *player_; // TODO
+    std::map<int, WorldEntity*> worldEntities_;
+    std::map<int, ControllableEntity*> controllabelEntities_;
+
+    void printLuaError(lua_State *lua);
+};
+
+}
+
+#endif // SCRIPTABLE_CONTROLLER_H

Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.cc
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.cc	                        (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.cc	2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,69 @@
+
+#include "scriptable_controller_api.h"
+#include <chrono>
+#include "lua.hpp"
+#include "luatb.h"
+#include "scriptable_controller.h"
+
+namespace orxonox
+{
+
+ScriptableControllerAPI *ScriptableControllerAPI::this_;
+
+ScriptableControllerAPI::ScriptableControllerAPI(lua_State *lua, ScriptableController *controller)
+{
+    this->lua_ = lua;
+    this->controller_ = controller;
+    ScriptableControllerAPI::this_ = this;
+
+    // Haven't found a shorter way yet to write that...
+    LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::testOutput)>::registerFunction<&ScriptableControllerAPI::testOutput>(this, lua, "testOutput");
+    LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAfterTimeout)>::registerFunction<&ScriptableControllerAPI::registerAfterTimeout>(this, lua, "registerAfterTimeout");
+    LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAtNearObject)>::registerFunction<&ScriptableControllerAPI::registerAtNearObject>(this, lua, "registerAtNearObject");
+    LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAtAreaEnter)>::registerFunction<&ScriptableControllerAPI::registerAtAreaEnter>(this, lua, "registerAtAreaEnter");
+    LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAtAreaLeave)>::registerFunction<&ScriptableControllerAPI::registerAtAreaLeave>(this, lua, "registerAtAreaLeave");
+    LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAtObjectDestroyed)>::registerFunction<&ScriptableControllerAPI::registerAtObjectDestroyed>(this, lua, "registerAtObjectDestroyed");
+    LuaTB<ScriptableControllerAPI, decltype(&ScriptableControllerAPI::registerAtPickup)>::registerFunction<&ScriptableControllerAPI::registerAtPickup>(this, lua, "registerAtPickup");
+}
+
+ScriptableControllerAPI::~ScriptableControllerAPI()
+{
+    lua_close(this->lua_);
+}
+
+void ScriptableControllerAPI::testOutput()
+{
+    orxout(user_info) << "Wheee!!!" << std::endl;
+}
+
+void ScriptableControllerAPI::registerAfterTimeout(std::function<void (void)> callback, int timeout_ms)
+{
+
+}
+
+int ScriptableControllerAPI::registerAtNearObject(std::function<void (int, int)> callback, int obj1, int obj2, double distance)
+{
+
+}
+
+int ScriptableControllerAPI::registerAtAreaEnter(std::function<void (int)> callback, int obj, int x, int y, int z, int dx, int dy, int dz)
+{
+
+}
+
+int ScriptableControllerAPI::registerAtAreaLeave(std::function<void (int)> callback, int obj, int x, int y, int z, int dx, int dy, int dz)
+{
+
+}
+
+int ScriptableControllerAPI::registerAtObjectDestroyed(std::function<void (int)> callback, int obj)
+{
+
+}
+
+int ScriptableControllerAPI::registerAtPickup(std::function<void (int)> callback, int pickup_id)
+{
+
+}
+
+}

Added: code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.h
===================================================================
--- code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.h	                        (rev 0)
+++ code/branches/ScriptableController_HS17/src/orxonox/scriptablecontroller/scriptable_controller_api.h	2017-10-30 15:06:11 UTC (rev 11519)
@@ -0,0 +1,44 @@
+#ifndef SCRIPTABLE_CONTROLLER_API_H
+#define SCRIPTABLE_CONTROLLER_API_H
+
+#include <functional>
+#include "core/CoreIncludes.h"
+
+// TODO Is pos int or double?
+
+struct lua_State;
+
+namespace orxonox
+{
+
+class ScriptableController;
+
+class ScriptableControllerAPI
+{
+public:
+    ScriptableControllerAPI(lua_State *lua, ScriptableController *controller);
+    ~ScriptableControllerAPI();
+
+    void testOutput(void);
+
+    void registerAfterTimeout(std::function<void (void)> callback, int timeout_ms);
+    int registerAtNearObject(std::function<void(int, int)> callback, int obj1, int obj2, double distance);
+    int registerAtAreaEnter(std::function<void (int)> callback, int obj, int x, int y, int z, int dx, int dy, int dz);
+    int registerAtAreaLeave(std::function<void (int)> callback, int obj, int x, int y, int z, int dx, int dy, int dz);
+    int registerAtObjectDestroyed(std::function<void (int)> callback, int obj);
+    int registerAtPickup(std::function<void (int)> callback, int pickup_id);
+
+    int destroyObject(int obj);
+    void removeObject(int obj);
+    int setObjectPosition(int obj, double x, double y, double z);
+
+    static ScriptableControllerAPI *this_;
+
+private:
+    lua_State *lua_;
+    ScriptableController *controller_;
+};
+
+}
+
+#endif // SCRIPTABLE_CONTROLLER_API_H



More information about the Orxonox-commit mailing list