diff --git a/apps/freeablo/fagui/console/console.cpp b/apps/freeablo/fagui/console/console.cpp index 76f1af9fb..0419c3b2c 100644 --- a/apps/freeablo/fagui/console/console.cpp +++ b/apps/freeablo/fagui/console/console.cpp @@ -10,10 +10,10 @@ namespace FAGui static const char* nullFile = "/dev/null"; #endif static char consoleStdoutBuffer[BUFSIZ]; - std::unique_ptr Console::mInstance = nullptr; + std::shared_ptr Console::mInstance = nullptr; static std::stringstream consoleCout; - Console::Console() : mBuffer({}), bufferLen(0), inputLen(0), mScript({}) + Console::Console() : mBuffer({}), bufferLen(0), inputLen(0), mScript(Script::LuaScript::getInstance()) { std::cout.rdbuf(consoleCout.rdbuf()); freopen(nullFile, "a", stdout); @@ -26,7 +26,7 @@ namespace FAGui fclose(stdout); } - std::unique_ptr& Console::getInstance() + std::shared_ptr Console::getInstance() { if (!mInstance) mInstance = std::unique_ptr(new Console()); @@ -61,7 +61,7 @@ namespace FAGui bufferLen += inputLen; inputLen = 0; - mScript.eval(command.c_str()); + mScript->eval(command.c_str()); std::string msg = ">> "; msg.append(consoleStdoutBuffer, strlen(consoleStdoutBuffer)); std::memset(consoleStdoutBuffer, '\0', BUFSIZ); diff --git a/apps/freeablo/fagui/console/console.h b/apps/freeablo/fagui/console/console.h index 24fda6ae7..139f2c9b1 100644 --- a/apps/freeablo/fagui/console/console.h +++ b/apps/freeablo/fagui/console/console.h @@ -15,11 +15,11 @@ namespace FAGui static constexpr size_t inputSize = 512; char mInput[inputSize]; int inputLen; - Script::LuaScript mScript; - static std::unique_ptr mInstance; + std::shared_ptr mScript; + static std::shared_ptr mInstance; public: - static std::unique_ptr& getInstance(); + static std::shared_ptr getInstance(); ~Console(); char* getInput() { return mInput; }; diff --git a/apps/freeablo/fagui/guimanager.cpp b/apps/freeablo/fagui/guimanager.cpp index 5252b11f4..1ed796306 100644 --- a/apps/freeablo/fagui/guimanager.cpp +++ b/apps/freeablo/fagui/guimanager.cpp @@ -530,7 +530,7 @@ namespace FAGui void GuiManager::consolePanel(nk_context* ctx) { - auto& c = Console::getInstance(); + auto c = Console::getInstance(); drawPanel(ctx, PanelType::console, [&]() { diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 15804d810..ce0e58545 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -157,5 +157,5 @@ add_library(Script script/luascript.h ) -target_link_libraries(Script lua Filesystem Misc) +target_link_libraries(Script lua LuaBridge Misc) set_target_properties(Script PROPERTIES COMPILE_FLAGS "${FA_COMPILER_FLAGS}") diff --git a/components/script/luascript.cpp b/components/script/luascript.cpp index 5388441ab..9452b4332 100644 --- a/components/script/luascript.cpp +++ b/components/script/luascript.cpp @@ -5,6 +5,8 @@ namespace Script { + std::shared_ptr LuaScript::mInstance = nullptr; + LuaScript::LuaScript() : mState(luaL_newstate()) { luaL_openlibs(mState); } LuaScript::~LuaScript() @@ -13,117 +15,6 @@ namespace Script lua_close(mState); } - void LuaScript::printError(const std::string& variable, const std::string& reason) - { -#ifdef NDEBUG - (void)variable; - (void)reason; -#else - std::cerr << "Can't get variable " << variable << ".\nReason: " << reason << "\n"; -#endif - } - - template <> std::string LuaScript::luaGetDefault() { return "null"; } - - template <> bool LuaScript::luaGet(const std::string& variable) - { - (void)variable; - return static_cast(lua_toboolean(mState, -1)); - } - template <> int LuaScript::luaGet(const std::string& variable) - { - (void)variable; - return static_cast(lua_tointeger(mState, -1)); - } - template <> int64_t LuaScript::luaGet(const std::string& variable) - { - (void)variable; - return static_cast(lua_tointeger(mState, -1)); - } - - template <> FixedPoint LuaScript::luaGet(const std::string& variable) - { - (void)variable; - return static_cast(luaL_checknumber(mState, 1)); - } - - template <> std::string LuaScript::luaGet(const std::string& variable) - { - std::string ret = "null"; - - release_assert(lua_isstring(mState, -1)); - - if (lua_isstring(mState, -1)) - ret = std::string(lua_tostring(mState, -1)); - else - printError(variable, "Not a string"); - - return ret; - } - - bool LuaScript::luaGetToStack(const std::string& variable, int& level) - { - level = 0; - std::string var{}; - - for (char c : variable) - { - if (c == '.') - { - if (level == 0) - { - lua_getglobal(mState, var.c_str()); - } - - else - { - lua_getfield(mState, -1, var.c_str()); - } - - if (lua_isnil(mState, -1)) - { - printError(variable, var + " is not defined"); - return false; - } - - else - { - var = ""; - ++level; - } - } - - else - { - var += c; - } - } - - if (level == 0) - { - lua_getglobal(mState, var.c_str()); - } - - else - { - lua_getfield(mState, -1, var.c_str()); - } - - if (lua_isnil(mState, -1)) - { - printError(variable, var + " is not defined"); - return false; - } - - return true; - } - - void LuaScript::clean() - { - int n = lua_gettop(mState); - lua_pop(mState, n); - } - void LuaScript::runScript(const std::string& path) { lua_settop(mState, 0); @@ -146,4 +37,7 @@ namespace Script lua_pop(mState, 1); } } + + template <> float LuaScript::get(const std::string &variable) = delete; + template <> double LuaScript::get(const std::string &variable) = delete; } diff --git a/components/script/luascript.h b/components/script/luascript.h index 782554995..b060dc02a 100644 --- a/components/script/luascript.h +++ b/components/script/luascript.h @@ -11,105 +11,53 @@ #include "lua.h" #include "lualib.h" +#include "LuaBridge/LuaBridge.h" +#include "LuaBridge/Vector.h" + namespace Script { class LuaScript { lua_State* mState; + static std::shared_ptr mInstance; public: - LuaScript(); ~LuaScript(); - template void registerGlobalType(); + template void registerType(); template void registerGlobalFunction(const std::string& functionName, Func&& func) { - lua_pushcfunction(mState, func); - lua_setglobal(mState, functionName.c_str()); + luabridge::getGlobalNamespace(mState).addFunction(functionName.c_str(), func); } - template void pushObject(T& obj, const char* varName) + template void pushGlobalObject(T& obj, const char* varName) { - static bool registered = false; - if (!registered) - { - registerGlobalType(); - registered = true; - } - - T** udata = reinterpret_cast(lua_newuserdata(mState, sizeof(T*))); - (*udata) = &obj; - const std::string typeName = typeid(T).name(); - luaL_setmetatable(mState, typeName.substr(1).c_str()); + registerType(); + luabridge::push(mState, obj); lua_setglobal(mState, varName); } template T get(const std::string& variable) { - release_assert(mState); - int level; - - T result; - if (luaGetToStack(variable, level)) - result = luaGet(variable); - - else - result = luaGetDefault(); - - lua_pop(mState, level + 1); - return result; - } - - template std::vector getVector(const std::string& variable) - { - std::vector ret; - lua_getglobal(mState, variable.c_str()); - if (lua_isnil(mState, -1)) - { - return {}; - } - - lua_pushnil(mState); - - while (lua_next(mState, -2)) - { - T aux; - if constexpr (std::is_same::value) - { - ret.push_back(lua_tostring(mState, -1)); - } - - else - { - ret.push_back(static_cast(lua_tonumber(mState, -1))); - } - - lua_pop(mState, 1); - } - - clean(); - return ret; + luabridge::LuaRef ret = luabridge::getGlobal(mState, variable.c_str()); + return ret.cast(); } void runScript(const std::string& path); void eval(const char* script); - private: - bool luaGetToStack(const std::string& variable, int& level); - - void clean(); - - void printError(const std::string& variable, const std::string& reason); + static std::shared_ptr getInstance() + { + if (!mInstance) + mInstance = std::shared_ptr(new LuaScript()); - template T luaGet(const std::string& variable = ""); + return mInstance; + } - template T luaGetDefault() { return {}; } + operator lua_State*() { return mState; } - template void pushFunction(Func&& f, const char* funcName) - { - lua_pushcfunction(mState, f); - lua_setfield(mState, -2, funcName); - } + private: + LuaScript(); }; } diff --git a/docs/cpp_class_to_lua.md b/docs/cpp_class_to_lua.md new file mode 100644 index 000000000..95bfb10b0 --- /dev/null +++ b/docs/cpp_class_to_lua.md @@ -0,0 +1,47 @@ +## Registering a C++ class into Lua + +Suppose you have the following class: + +```cpp +class Greeter +{ + const char* msg +public: + Greeter() {} + Greeter(const char *msg) {} + void hello(const char* name) { std::cout << "Hello, " << name << "\n"; } + void print() { std::cout << msg << "\n"; } +}; +``` + +If you want to use it inside Lua, you should implement Script::LuaScript::registerType for it: + +```cpp +namespace Script +{ + template <> void LuaScript::registerType() + { + luabridge::getGlobalNamespace(mState) + .beginNamespace("example") // optionally, you can wrap the class inside a namespace + .beginClass("Greeter") + .addConstructor() + .addConstructor() + .addFunction("hello", &Greeter::hello) + .addFunction("print", &Greeter::print) + .endClass() + .endNamespace(); + } +} +``` + +Make sure you call 'registerType' before using the class in Lua. Now, to use in Lua: + +```lua +local greeter = example.Greeter() +greeter:hello("John Smith") + +local greeter2 = example.Greeter("Hello world") +greeter2:print() +``` + +For more information, see [LuaBridge Reference](https://vinniefalco.github.io/LuaBridge/Manual.html). diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 573f1f3b4..28f14d8f7 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -23,5 +23,6 @@ add_subdirectory(SDL_image) add_subdirectory(StormLib) add_subdirectory(gtest) add_subdirectory(lua) +add_subdirectory(LuaBridge) unset(MESSAGE_QUIET) diff --git a/extern/LuaBridge/Source/LuaBridge/detail/LuaHelpers.h b/extern/LuaBridge/Source/LuaBridge/detail/LuaHelpers.h index e336cf2b2..1915e4b0d 100644 --- a/extern/LuaBridge/Source/LuaBridge/detail/LuaHelpers.h +++ b/extern/LuaBridge/Source/LuaBridge/detail/LuaHelpers.h @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ /* https://github.com/vinniefalco/LuaBridge - + Copyright 2012, Vinnie Falco Copyright 2007, Nathan Reed @@ -94,7 +94,7 @@ inline int get_length (lua_State* L, int idx) inline int get_length (lua_State* L, int idx) { lua_len (L, idx); - int len = int (luaL_checknumber (L, -1)); + int len = int (luaL_checknumber (L, -1).intPart()); lua_pop (L, 1); return len; } @@ -108,7 +108,7 @@ inline int get_length (lua_State* L, int idx) #endif /** Get a table value, bypassing metamethods. -*/ +*/ inline void rawgetfield (lua_State* L, int index, char const* key) { assert (lua_istable (L, index)); @@ -118,7 +118,7 @@ inline void rawgetfield (lua_State* L, int index, char const* key) } /** Set a table value, bypassing metamethods. -*/ +*/ inline void rawsetfield (lua_State* L, int index, char const* key) { assert (lua_istable (L, index)); diff --git a/extern/LuaBridge/Source/LuaBridge/detail/Stack.h b/extern/LuaBridge/Source/LuaBridge/detail/Stack.h index f00b9c9df..abb18704e 100644 --- a/extern/LuaBridge/Source/LuaBridge/detail/Stack.h +++ b/extern/LuaBridge/Source/LuaBridge/detail/Stack.h @@ -95,7 +95,7 @@ struct Stack { lua_pushinteger (L, static_cast (value)); } - + static int get (lua_State* L, int index) { return static_cast (luaL_checkinteger (L, index)); @@ -118,7 +118,7 @@ struct Stack { lua_pushinteger (L, static_cast (value)); } - + static unsigned int get (lua_State* L, int index) { return static_cast (luaL_checkinteger (L, index)); @@ -141,7 +141,7 @@ struct Stack { lua_pushinteger (L, static_cast (value)); } - + static unsigned char get (lua_State* L, int index) { return static_cast (luaL_checkinteger (L, index)); @@ -164,7 +164,7 @@ struct Stack { lua_pushinteger (L, static_cast (value)); } - + static short get (lua_State* L, int index) { return static_cast (luaL_checkinteger (L, index)); @@ -187,7 +187,7 @@ struct Stack { lua_pushinteger (L, static_cast (value)); } - + static unsigned short get (lua_State* L, int index) { return static_cast (luaL_checkinteger (L, index)); @@ -210,7 +210,7 @@ struct Stack { lua_pushinteger (L, static_cast (value)); } - + static long get (lua_State* L, int index) { return static_cast (luaL_checkinteger (L, index)); @@ -233,7 +233,7 @@ struct Stack { lua_pushinteger (L, static_cast (value)); } - + static unsigned long get (lua_State* L, int index) { return static_cast (luaL_checkinteger (L, index)); @@ -290,44 +290,21 @@ struct Stack } }; -//------------------------------------------------------------------------------ -/** - Stack specialization for `float`. -*/ -template <> -struct Stack -{ - static void push (lua_State* L, float value) - { - lua_pushnumber (L, static_cast (value)); - } - - static float get (lua_State* L, int index) - { - return static_cast (luaL_checknumber (L, index)); - } - - static bool isInstance (lua_State* L, int index) - { - return lua_type (L, index) == LUA_TNUMBER; - } -}; - //------------------------------------------------------------------------------ /** Stack specialization for `double`. */ template <> -struct Stack +struct Stack { - static void push (lua_State* L, double value) + static void push (lua_State* L, FixedPoint value) { - lua_pushnumber (L, static_cast (value)); + lua_pushnumber (L, value); } - - static double get (lua_State* L, int index) + + static FixedPoint get (lua_State* L, int index) { - return static_cast (luaL_checknumber (L, index)); + return luaL_checknumber (L, index); } static bool isInstance (lua_State* L, int index) @@ -347,7 +324,7 @@ struct Stack { lua_pushboolean (L, value ? 1 : 0); } - + static bool get (lua_State* L, int index) { return lua_toboolean (L, index) ? true : false; @@ -370,7 +347,7 @@ struct Stack { lua_pushlstring (L, &value, 1); } - + static char get (lua_State* L, int index) { return luaL_checkstring (L, index) [0];